Skip to content

Commit

Permalink
metal: Misc. improvements.
Browse files Browse the repository at this point in the history
- Use a single buffer for various non-changing constants accessed by the GPU, instead of multiple buffers.
- Do the half-pixel offset for points and lines using a transform matrix so we don't need a malloc when rendering.
- Don't add a half-pixel offset for other primitives and textures. This matches D3D and GL render behaviour.
- Remove the half-texel texture coordinate offset since it's not needed now that there's no more half-pixel position offset when rendering a texture.
- Don't try to set texture usage on iOS 8 since it doesn't exist there.
  • Loading branch information
slime73 committed Jan 3, 2018
1 parent f9cd765 commit 888198e
Show file tree
Hide file tree
Showing 4 changed files with 1,053 additions and 989 deletions.
138 changes: 76 additions & 62 deletions src/render/metal/SDL_render_metal.m
Expand Up @@ -105,6 +105,20 @@ static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect
}
};

/* macOS requires constants in a buffer to have a 256 byte alignment. */
#ifdef __MACOSX__
#define CONSTANT_ALIGN 256
#else
#define CONSTANT_ALIGN 4
#endif

#define ALIGN_CONSTANTS(size) ((size + CONSTANT_ALIGN - 1) & (~(CONSTANT_ALIGN - 1)))

static const size_t CONSTANTS_OFFSET_IDENTITY = 0;
static const size_t CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM = ALIGN_CONSTANTS(CONSTANTS_OFFSET_IDENTITY + sizeof(float) * 16);
static const size_t CONSTANTS_OFFSET_CLEAR_VERTS = ALIGN_CONSTANTS(CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM + sizeof(float) * 16);
static const size_t CONSTANTS_LENGTH = CONSTANTS_OFFSET_CLEAR_VERTS + sizeof(float) * 6;

typedef enum SDL_MetalVertexFunction
{
SDL_METAL_VERTEX_SOLID,
Expand Down Expand Up @@ -144,8 +158,7 @@ @interface METAL_RenderData : NSObject
@property (nonatomic, assign) METAL_PipelineCache *mtlpipelinecopy;
@property (nonatomic, retain) id<MTLSamplerState> mtlsamplernearest;
@property (nonatomic, retain) id<MTLSamplerState> mtlsamplerlinear;
@property (nonatomic, retain) id<MTLBuffer> mtlbufclearverts;
@property (nonatomic, retain) id<MTLBuffer> mtlbufidentitytransform;
@property (nonatomic, retain) id<MTLBuffer> mtlbufconstants;
@property (nonatomic, retain) CAMetalLayer *mtllayer;
@property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
@end
Expand All @@ -162,8 +175,7 @@ - (void)dealloc
[_mtlbackbuffer release];
[_mtlsamplernearest release];
[_mtlsamplerlinear release];
[_mtlbufclearverts release];
[_mtlbufidentitytransform release];
[_mtlbufconstants release];
[_mtllayer release];
[_mtlpassdesc release];
[super dealloc];
Expand Down Expand Up @@ -460,17 +472,39 @@ - (void)dealloc
id<MTLSamplerState> mtlsamplerlinear = [data.mtldevice newSamplerStateWithDescriptor:samplerdesc];
data.mtlsamplerlinear = mtlsamplerlinear;

static const float clearverts[] = { 0, 0, 0, 3, 3, 0 };
id<MTLBuffer> mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined];
data.mtlbufclearverts = mtlbufclearverts;
data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
/* Note: matrices are column major. */
float identitytransform[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};

float halfpixeltransform[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f,
};

float clearverts[6] = {0.0f, 0.0f, 0.0f, 2.0f, 2.0f, 0.0f};

MTLResourceOptions constantsopts = 0;
#ifdef __MACOSX__
constantsopts |= MTLResourceStorageModeManaged;
#endif

id<MTLBuffer> mtlbufconstants = [data.mtldevice newBufferWithLength:CONSTANTS_LENGTH options:constantsopts];
data.mtlbufconstants = mtlbufconstants;
data.mtlbufconstants.label = @"SDL constant data";

float identitytx[16];
SDL_memset(identitytx, 0, sizeof(identitytx));
identitytx[0] = identitytx[5] = identitytx[10] = identitytx[15] = 1.0f;
id<MTLBuffer> mtlbufidentitytransform = [data.mtldevice newBufferWithBytes:identitytx length:sizeof(identitytx) options:0];
data.mtlbufidentitytransform = mtlbufidentitytransform;
data.mtlbufidentitytransform.label = @"SDL_RenderCopy identity transform";
char *constantdata = [data.mtlbufconstants contents];
SDL_memcpy(constantdata + CONSTANTS_OFFSET_IDENTITY, identitytransform, sizeof(identitytransform));
SDL_memcpy(constantdata + CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM, halfpixeltransform, sizeof(halfpixeltransform));
SDL_memcpy(constantdata + CONSTANTS_OFFSET_CLEAR_VERTS, clearverts, sizeof(clearverts));
#ifdef __MACOSX__
[data.mtlbufconstants didModifyRange:NSMakeRange(0, CONSTANTS_LENGTH)];
#endif

// !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.

Expand Down Expand Up @@ -516,8 +550,7 @@ - (void)dealloc
[samplerdesc release];
[mtlsamplernearest release];
[mtlsamplerlinear release];
[mtlbufclearverts release];
[mtlbufidentitytransform release];
[mtlbufconstants release];
[view release];
[data release];
#ifdef __MACOSX__
Expand Down Expand Up @@ -607,11 +640,14 @@ - (void)dealloc

MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];

if (texture->access == SDL_TEXTUREACCESS_TARGET) {
mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
} else {
mtltexdesc.usage = MTLTextureUsageShaderRead;

/* Not available in iOS 8. */
if ([mtltexdesc respondsToSelector:@selector(usage)]) {
if (texture->access == SDL_TEXTUREACCESS_TARGET) {
mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
} else {
mtltexdesc.usage = MTLTextureUsageShaderRead;
}
}
//mtltexdesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
//mtltexdesc.storageMode = MTLStorageModeManaged;
Expand Down Expand Up @@ -796,7 +832,8 @@ - (void)dealloc
METAL_SetOrthographicProjection(renderer, 1, 1);
[data.mtlcmdencoder setViewport:viewport];
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelineprims, SDL_BLENDMODE_NONE)];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_CLEAR_VERTS atIndex:0];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
[data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3];

Expand All @@ -813,24 +850,11 @@ - (void)dealloc
return 0;
}}

// adjust pixel center for x and y coordinates
static inline float
adjustx(const float val)
{
return (val + 0.5f);
}
static inline float
adjusty(const float val)
{
return (val + 0.5f);
}

// normalize a value from 0.0f to len into 0.0f to 1.0f.
static inline float
normtex(const float _val, const float len)
{
const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
return ((val + 0.5f) / len);
return _val / len;
}

static int
Expand All @@ -840,11 +864,6 @@ - (void)dealloc
METAL_ActivateRenderer(renderer);

const size_t vertlen = (sizeof (float) * 2) * count;
float *verts = SDL_malloc(vertlen);
if (!verts) {
return SDL_OutOfMemory();
}

METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;

// !!! FIXME: render color should live in a dedicated uniform buffer.
Expand All @@ -853,16 +872,10 @@ - (void)dealloc
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];

float *ptr = verts;
for (int i = 0; i < count; i++, points++) {
*ptr = adjustx(points->x); ptr++;
*ptr = adjusty(points->y); ptr++;
}

[data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
[data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_HALF_PIXEL_TRANSFORM atIndex:3];
[data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];

SDL_free(verts);
return 0;
}}

Expand All @@ -889,15 +902,16 @@ - (void)dealloc

[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];

for (int i = 0; i < count; i++, rects++) {
if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;

const float verts[] = {
adjustx(rects->x), adjusty(rects->y + rects->h),
adjustx(rects->x), adjusty(rects->y),
adjustx(rects->x + rects->w), adjusty(rects->y + rects->h),
adjustx(rects->x + rects->w), adjusty(rects->y)
rects->x, rects->y + rects->h,
rects->x, rects->y,
rects->x + rects->w, rects->y + rects->h,
rects->x + rects->w, rects->y
};

[data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
Expand All @@ -918,10 +932,10 @@ - (void)dealloc
const float texh = (float) texturedata.mtltexture.height;

const float xy[] = {
adjustx(dstrect->x), adjusty(dstrect->y + dstrect->h),
adjustx(dstrect->x), adjusty(dstrect->y),
adjustx(dstrect->x + dstrect->w), adjusty(dstrect->y + dstrect->h),
adjustx(dstrect->x + dstrect->w), adjusty(dstrect->y)
dstrect->x, dstrect->y + dstrect->h,
dstrect->x, dstrect->y,
dstrect->x + dstrect->w, dstrect->y + dstrect->h,
dstrect->x + dstrect->w, dstrect->y
};

const float uv[] = {
Expand All @@ -942,7 +956,7 @@ - (void)dealloc
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data, data.mtlpipelinecopy, texture->blendMode)];
[data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
[data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufidentitytransform offset:0 atIndex:3];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufconstants offset:CONSTANTS_OFFSET_IDENTITY atIndex:3];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
[data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
[data.mtlcmdencoder setFragmentSamplerState:texturedata.mtlsampler atIndex:0];
Expand Down Expand Up @@ -988,10 +1002,10 @@ - (void)dealloc
};

const float xy[] = {
adjustx(-center->x), adjusty(dstrect->h - center->y),
adjustx(-center->x), adjusty(-center->y),
adjustx(dstrect->w - center->x), adjusty(dstrect->h - center->y),
adjustx(dstrect->w - center->x), adjusty(-center->y)
-center->x, dstrect->h - center->y,
-center->x, -center->y,
dstrect->w - center->x, dstrect->h - center->y,
dstrect->w - center->x, -center->y
};

{
Expand Down
3 changes: 2 additions & 1 deletion src/render/metal/SDL_shaders_metal.metal
Expand Up @@ -11,10 +11,11 @@ struct SolidVertexOutput

vertex SolidVertexOutput SDL_Solid_vertex(const device float2 *position [[buffer(0)]],
constant float4x4 &projection [[buffer(2)]],
constant float4x4 &transform [[buffer(3)]],
uint vid [[vertex_id]])
{
SolidVertexOutput v;
v.position = projection * float4(position[vid], 0.0f, 1.0f);
v.position = (projection * transform) * float4(position[vid], 0.0f, 1.0f);
v.pointSize = 0.5f;
return v;
}
Expand Down

0 comments on commit 888198e

Please sign in to comment.