|
@@ -15,6 +15,7 @@
|
|
//
|
|
//
|
|
|
|
|
|
#import "CSDisplayMetal.h"
|
|
#import "CSDisplayMetal.h"
|
|
|
|
+#import "UTMShaderTypes.h"
|
|
#import <glib.h>
|
|
#import <glib.h>
|
|
#import <spice-client.h>
|
|
#import <spice-client.h>
|
|
#import <spice/protocol.h>
|
|
#import <spice/protocol.h>
|
|
@@ -28,15 +29,20 @@
|
|
@implementation CSDisplayMetal {
|
|
@implementation CSDisplayMetal {
|
|
SpiceDisplayChannel *_display;
|
|
SpiceDisplayChannel *_display;
|
|
|
|
|
|
- gint _mark;
|
|
|
|
|
|
+ BOOL _sigsconnected;
|
|
|
|
+
|
|
|
|
+ /* Modifying the following REQUIRES lock held */
|
|
|
|
+ //gint _mark;
|
|
gint _canvasFormat;
|
|
gint _canvasFormat;
|
|
gint _canvasStride;
|
|
gint _canvasStride;
|
|
- void *_canvasData;
|
|
|
|
|
|
+ const void *_canvasData;
|
|
CGRect _canvasArea;
|
|
CGRect _canvasArea;
|
|
CGRect _visibleArea;
|
|
CGRect _visibleArea;
|
|
- GWeakRef _overlay_weak_ref;
|
|
|
|
-
|
|
|
|
- BOOL _sigsconnected;
|
|
|
|
|
|
+ GWeakRef _overlay_weak_ref;
|
|
|
|
+ id<MTLDevice> _device;
|
|
|
|
+ id<MTLTexture> _texture;
|
|
|
|
+ id<MTLBuffer> _vertices;
|
|
|
|
+ NSUInteger _numVertices;
|
|
}
|
|
}
|
|
|
|
|
|
static void cs_primary_create(SpiceChannel *channel, gint format,
|
|
static void cs_primary_create(SpiceChannel *channel, gint format,
|
|
@@ -44,10 +50,13 @@ static void cs_primary_create(SpiceChannel *channel, gint format,
|
|
gint shmid, gpointer imgdata, gpointer data) {
|
|
gint shmid, gpointer imgdata, gpointer data) {
|
|
CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
|
|
|
|
- self->_canvasArea = CGRectMake(0, 0, width, height);
|
|
|
|
- self->_canvasFormat = format;
|
|
|
|
- self->_canvasStride = stride;
|
|
|
|
- self->_canvasData = imgdata;
|
|
|
|
|
|
+ g_assert(format == SPICE_SURFACE_FMT_32_xRGB || format == SPICE_SURFACE_FMT_16_555);
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ self->_canvasArea = CGRectMake(0, 0, width, height);
|
|
|
|
+ self->_canvasFormat = format;
|
|
|
|
+ self->_canvasStride = stride;
|
|
|
|
+ self->_canvasData = imgdata;
|
|
|
|
+ }
|
|
|
|
|
|
cs_update_monitor_area(channel, data);
|
|
cs_update_monitor_area(channel, data);
|
|
}
|
|
}
|
|
@@ -55,21 +64,31 @@ static void cs_primary_create(SpiceChannel *channel, gint format,
|
|
static void cs_primary_destroy(SpiceDisplayChannel *channel, gpointer data) {
|
|
static void cs_primary_destroy(SpiceDisplayChannel *channel, gpointer data) {
|
|
CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
self.ready = NO;
|
|
self.ready = NO;
|
|
- self->_canvasArea = CGRectZero;
|
|
|
|
- self->_canvasFormat = 0;
|
|
|
|
- self->_canvasStride = 0;
|
|
|
|
- self->_canvasData = NULL;
|
|
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ self->_canvasArea = CGRectZero;
|
|
|
|
+ self->_canvasFormat = 0;
|
|
|
|
+ self->_canvasStride = 0;
|
|
|
|
+ self->_canvasData = NULL;
|
|
|
|
+ self->_texture = nil;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static void cs_invalidate(SpiceChannel *channel,
|
|
static void cs_invalidate(SpiceChannel *channel,
|
|
gint x, gint y, gint w, gint h, gpointer data) {
|
|
gint x, gint y, gint w, gint h, gpointer data) {
|
|
CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
-#warning Unimplemented
|
|
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ CGRect rect = CGRectIntersection(CGRectMake(x, y, w, h), self->_visibleArea);
|
|
|
|
+ if (!CGRectIsEmpty(rect)) {
|
|
|
|
+ [self drawRegion:rect];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
static void cs_mark(SpiceChannel *channel, gint mark, gpointer data) {
|
|
static void cs_mark(SpiceChannel *channel, gint mark, gpointer data) {
|
|
- CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
|
|
- self->_mark = mark;
|
|
|
|
|
|
+ //CSDisplayMetal *self = (__bridge CSDisplayMetal *)data;
|
|
|
|
+ //@synchronized (self) {
|
|
|
|
+ // self->_mark = mark; // currently this does nothing for us
|
|
|
|
+ //}
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean cs_set_overlay(SpiceChannel *channel, void* pipeline_ptr, gpointer data) {
|
|
static gboolean cs_set_overlay(SpiceChannel *channel, void* pipeline_ptr, gpointer data) {
|
|
@@ -194,6 +213,22 @@ static void cs_channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+- (void)setDevice:(id<MTLDevice>)device {
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ _device = device;
|
|
|
|
+ }
|
|
|
|
+ [self rebuildTexture];
|
|
|
|
+ [self rebuildVertices];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (id<MTLDevice>)device {
|
|
|
|
+ return _device;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@synthesize texture;
|
|
|
|
+@synthesize numVertices;
|
|
|
|
+@synthesize vertices;
|
|
|
|
+
|
|
- (id)initWithSession:(nonnull SpiceSession *)session channelID:(NSInteger)channelID monitorID:(NSInteger)monitorID {
|
|
- (id)initWithSession:(nonnull SpiceSession *)session channelID:(NSInteger)channelID monitorID:(NSInteger)monitorID {
|
|
self = [self init];
|
|
self = [self init];
|
|
if (self) {
|
|
if (self) {
|
|
@@ -236,13 +271,72 @@ static void cs_channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer
|
|
}
|
|
}
|
|
|
|
|
|
- (void)updateVisibleAreaWithRect:(CGRect)rect {
|
|
- (void)updateVisibleAreaWithRect:(CGRect)rect {
|
|
- CGRect visible = CGRectIntersection(_canvasArea, rect);
|
|
|
|
- if (CGRectIsNull(visible)) {
|
|
|
|
- DISPLAY_DEBUG(self, "The monitor area is not intersecting primary surface");
|
|
|
|
- self.ready = NO;
|
|
|
|
- _visibleArea = CGRectZero;
|
|
|
|
- } else {
|
|
|
|
- _visibleArea = visible;
|
|
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ CGRect visible = CGRectIntersection(_canvasArea, rect);
|
|
|
|
+ if (CGRectIsNull(visible)) {
|
|
|
|
+ DISPLAY_DEBUG(self, "The monitor area is not intersecting primary surface");
|
|
|
|
+ self.ready = NO;
|
|
|
|
+ _visibleArea = CGRectZero;
|
|
|
|
+ } else {
|
|
|
|
+ _visibleArea = visible;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ [self rebuildTexture];
|
|
|
|
+ [self rebuildVertices];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)rebuildTexture {
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ if (CGRectIsEmpty(_canvasArea) || !_device) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ MTLTextureDescriptor *textureDescriptor = [[MTLTextureDescriptor alloc] init];
|
|
|
|
+ // don't worry that that components are reversed, we fix it in shaders
|
|
|
|
+ textureDescriptor.pixelFormat = (_canvasFormat == SPICE_SURFACE_FMT_32_xRGB) ? MTLPixelFormatBGRA8Unorm : MTLPixelFormatBGR5A1Unorm;
|
|
|
|
+ textureDescriptor.width = _visibleArea.size.width;
|
|
|
|
+ textureDescriptor.height = _visibleArea.size.height;
|
|
|
|
+ _texture = [_device newTextureWithDescriptor:textureDescriptor];
|
|
|
|
+ [self drawRegion:_visibleArea];
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)rebuildVertices {
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ // We flip the y-coordinates because pixman renders flipped
|
|
|
|
+ UTMVertex quadVertices[] =
|
|
|
|
+ {
|
|
|
|
+ // Pixel positions, Texture coordinates
|
|
|
|
+ { { _visibleArea.size.width/2, _visibleArea.size.height/2 }, { 1.f, 0.f } },
|
|
|
|
+ { { -_visibleArea.size.width/2, _visibleArea.size.height/2 }, { 0.f, 0.f } },
|
|
|
|
+ { { -_visibleArea.size.width/2, -_visibleArea.size.height/2 }, { 0.f, 1.f } },
|
|
|
|
+
|
|
|
|
+ { { _visibleArea.size.width/2, _visibleArea.size.height/2 }, { 1.f, 0.f } },
|
|
|
|
+ { { -_visibleArea.size.width/2, -_visibleArea.size.height/2 }, { 0.f, 1.f } },
|
|
|
|
+ { { _visibleArea.size.width/2, -_visibleArea.size.height/2 }, { 1.f, 1.f } },
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // Create our vertex buffer, and initialize it with our quadVertices array
|
|
|
|
+ _vertices = [_device newBufferWithBytes:quadVertices
|
|
|
|
+ length:sizeof(quadVertices)
|
|
|
|
+ options:MTLResourceStorageModeShared];
|
|
|
|
+
|
|
|
|
+ // Calculate the number of vertices by dividing the byte length by the size of each vertex
|
|
|
|
+ _numVertices = sizeof(quadVertices) / sizeof(UTMVertex);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+- (void)drawRegion:(CGRect)rect {
|
|
|
|
+ @synchronized (self) {
|
|
|
|
+ NSInteger pixelSize = (_canvasFormat == SPICE_SURFACE_FMT_32_xRGB) ? 4 : 2;
|
|
|
|
+ // copy texture
|
|
|
|
+ MTLRegion region = {
|
|
|
|
+ { rect.origin.x-_visibleArea.origin.x, rect.origin.y-_visibleArea.origin.y, 0 }, // MTLOrigin
|
|
|
|
+ { rect.size.width, rect.size.height, 1} // MTLSize
|
|
|
|
+ };
|
|
|
|
+ [_texture replaceRegion:region
|
|
|
|
+ mipmapLevel:0
|
|
|
|
+ withBytes:(const char *)_canvasData + (NSUInteger)(rect.origin.y*_canvasStride + rect.origin.x*pixelSize)
|
|
|
|
+ bytesPerRow:_canvasStride];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|