metal: use a projection matrix instead of manually transforming vertices into clip space on the CPU.

This commit is contained in:
Alex Szpakowski
2017-12-30 20:32:22 -04:00
parent 49df65c25a
commit e24dc9053b
4 changed files with 1801 additions and 1710 deletions

View File

@@ -324,7 +324,7 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
data.mtlpipelinecopylinear = [[NSMutableArray alloc] init];
MakePipelineStates(data, data.mtlpipelinecopylinear, @"SDL texture pipeline (linear)", @"SDL_Copy_vertex", @"SDL_Copy_fragment_linear");
static const float clearverts[] = { -1, -1, -1, 3, 3, -1 };
static const float clearverts[] = { 0, 0, 0, 3, 3, 0 };
data.mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined];
data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
@@ -504,6 +504,39 @@ METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
return 0;
}}
static int
METAL_SetOrthographicProjection(SDL_Renderer *renderer, int w, int h)
{ @autoreleasepool {
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
float projection[4][4];
if (!w || !h) {
return 0;
}
/* Prepare an orthographic projection */
projection[0][0] = 2.0f / w;
projection[0][1] = 0.0f;
projection[0][2] = 0.0f;
projection[0][3] = 0.0f;
projection[1][0] = 0.0f;
projection[1][1] = -2.0f / h;
projection[1][2] = 0.0f;
projection[1][3] = 0.0f;
projection[2][0] = 0.0f;
projection[2][1] = 0.0f;
projection[2][2] = 0.0f;
projection[2][3] = 0.0f;
projection[3][0] = -1.0f;
projection[3][1] = 1.0f;
projection[3][2] = 0.0f;
projection[3][3] = 1.0f;
// !!! FIXME: This should be in a buffer...
[data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
return 0;
}}
static int
METAL_UpdateViewport(SDL_Renderer * renderer)
{ @autoreleasepool {
@@ -517,6 +550,7 @@ METAL_UpdateViewport(SDL_Renderer * renderer)
viewport.znear = 0.0;
viewport.zfar = 1.0;
[data.mtlcmdencoder setViewport:viewport];
METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
return 0;
}}
@@ -563,6 +597,7 @@ METAL_RenderClear(SDL_Renderer * renderer)
viewport.zfar = 1.0;
// Draw a simple filled fullscreen triangle now.
METAL_SetOrthographicProjection(renderer, 1, 1);
[data.mtlcmdencoder setViewport:viewport];
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
@@ -577,26 +612,11 @@ METAL_RenderClear(SDL_Renderer * renderer)
viewport.znear = 0.0;
viewport.zfar = 1.0;
[data.mtlcmdencoder setViewport:viewport];
METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
return 0;
}}
// normalize a value from 0.0f to len into -1.0f to 1.0f.
static inline float
normx(const float _val, const float len)
{
const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
return (((val + 0.5f) / len) * 2.0f) - 1.0f;
}
// normalize a value from 0.0f to len into -1.0f to 1.0f.
static inline float
normy(const float _val, const float len)
{
const float val = (_val <= 0.0f) ? len : (_val >= len) ? 0.0f : (len - _val);
return (((val - 0.5f) / len) * 2.0f) - 1.0f;
}
// normalize a value from 0.0f to len into 0.0f to 1.0f.
static inline float
normtex(const float _val, const float len)
@@ -611,12 +631,7 @@ DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
{ @autoreleasepool {
METAL_ActivateRenderer(renderer);
const size_t vertlen = (sizeof (float) * 2) * count;
float *verts = SDL_malloc(vertlen);
if (!verts) {
return SDL_OutOfMemory();
}
const size_t vertlen = sizeof(SDL_FPoint) * count;
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
// !!! FIXME: render color should live in a dedicated uniform buffer.
@@ -624,21 +639,8 @@ DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
const float w = (float)renderer->viewport.w;
const float h = (float)renderer->viewport.h;
// !!! FIXME: we can convert this in the shader. This will save the malloc and for-loop, but we still need to upload.
float *ptr = verts;
for (int i = 0; i < count; i++, points++) {
*ptr = normx(points->x, w); ptr++;
*ptr = normy(points->y, h); ptr++;
}
[data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
[data.mtlcmdencoder setVertexBytes:points length:vertlen atIndex:0];
[data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
SDL_free(verts);
return 0;
}}
@@ -666,17 +668,14 @@ METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int coun
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
const float w = (float)renderer->viewport.w;
const float h = (float)renderer->viewport.h;
for (int i = 0; i < count; i++, rects++) {
if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
const float verts[] = {
normx(rects->x, w), normy(rects->y + rects->h, h),
normx(rects->x, w), normy(rects->y, h),
normx(rects->x + rects->w, w), normy(rects->y + rects->h, h),
normx(rects->x + rects->w, w), normy(rects->y, h)
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];
@@ -693,16 +692,14 @@ METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
METAL_ActivateRenderer(renderer);
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->driverdata;
const float w = (float)renderer->viewport.w;
const float h = (float)renderer->viewport.h;
const float texw = (float) texturedata.mtltexture.width;
const float texh = (float) texturedata.mtltexture.height;
const float xy[] = {
normx(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
normx(dstrect->x, w), normy(dstrect->y, h),
normx(dstrect->x + dstrect->w, w), normy(dstrect->y + dstrect->h, h),
normx(dstrect->x + dstrect->w, w), normy(dstrect->y, h)
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[] = {