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]; data.mtlpipelinecopylinear = [[NSMutableArray alloc] init];
MakePipelineStates(data, data.mtlpipelinecopylinear, @"SDL texture pipeline (linear)", @"SDL_Copy_vertex", @"SDL_Copy_fragment_linear"); 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 = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined];
data.mtlbufclearverts.label = @"SDL_RenderClear vertices"; data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
@ -504,6 +504,39 @@ METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
return 0; 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 static int
METAL_UpdateViewport(SDL_Renderer * renderer) METAL_UpdateViewport(SDL_Renderer * renderer)
{ @autoreleasepool { { @autoreleasepool {
@ -517,6 +550,7 @@ METAL_UpdateViewport(SDL_Renderer * renderer)
viewport.znear = 0.0; viewport.znear = 0.0;
viewport.zfar = 1.0; viewport.zfar = 1.0;
[data.mtlcmdencoder setViewport:viewport]; [data.mtlcmdencoder setViewport:viewport];
METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
return 0; return 0;
}} }}
@ -563,6 +597,7 @@ METAL_RenderClear(SDL_Renderer * renderer)
viewport.zfar = 1.0; viewport.zfar = 1.0;
// Draw a simple filled fullscreen triangle now. // Draw a simple filled fullscreen triangle now.
METAL_SetOrthographicProjection(renderer, 1, 1);
[data.mtlcmdencoder setViewport:viewport]; [data.mtlcmdencoder setViewport:viewport];
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)]; [data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0]; [data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
@ -577,26 +612,11 @@ METAL_RenderClear(SDL_Renderer * renderer)
viewport.znear = 0.0; viewport.znear = 0.0;
viewport.zfar = 1.0; viewport.zfar = 1.0;
[data.mtlcmdencoder setViewport:viewport]; [data.mtlcmdencoder setViewport:viewport];
METAL_SetOrthographicProjection(renderer, renderer->viewport.w, renderer->viewport.h);
return 0; 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. // normalize a value from 0.0f to len into 0.0f to 1.0f.
static inline float static inline float
normtex(const float _val, const float len) normtex(const float _val, const float len)
@ -611,12 +631,7 @@ DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
{ @autoreleasepool { { @autoreleasepool {
METAL_ActivateRenderer(renderer); METAL_ActivateRenderer(renderer);
const size_t vertlen = (sizeof (float) * 2) * count; const size_t vertlen = sizeof(SDL_FPoint) * count;
float *verts = SDL_malloc(vertlen);
if (!verts) {
return SDL_OutOfMemory();
}
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
// !!! FIXME: render color should live in a dedicated uniform buffer. // !!! 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 setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0]; [data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
[data.mtlcmdencoder setVertexBytes:points length:vertlen 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 drawPrimitives:primtype vertexStart:0 vertexCount:count]; [data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
SDL_free(verts);
return 0; 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 setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0]; [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++) { for (int i = 0; i < count; i++, rects++) {
if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue; if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
const float verts[] = { const float verts[] = {
normx(rects->x, w), normy(rects->y + rects->h, h), rects->x, rects->y + rects->h,
normx(rects->x, w), normy(rects->y, h), rects->x, rects->y,
normx(rects->x + rects->w, w), normy(rects->y + rects->h, h), rects->x + rects->w, rects->y + rects->h,
normx(rects->x + rects->w, w), normy(rects->y, h) rects->x + rects->w, rects->y,
}; };
[data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0]; [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_ActivateRenderer(renderer);
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
METAL_TextureData *texturedata = (__bridge METAL_TextureData *)texture->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 texw = (float) texturedata.mtltexture.width;
const float texh = (float) texturedata.mtltexture.height; const float texh = (float) texturedata.mtltexture.height;
const float xy[] = { const float xy[] = {
normx(dstrect->x, w), normy(dstrect->y + dstrect->h, h), dstrect->x, dstrect->y + dstrect->h,
normx(dstrect->x, w), normy(dstrect->y, h), dstrect->x, dstrect->y,
normx(dstrect->x + dstrect->w, w), normy(dstrect->y + dstrect->h, h), dstrect->x + dstrect->w, dstrect->y + dstrect->h,
normx(dstrect->x + dstrect->w, w), normy(dstrect->y, h) dstrect->x + dstrect->w, dstrect->y
}; };
const float uv[] = { const float uv[] = {

View File

@ -1,17 +1,20 @@
#include <metal_texture> #include <metal_texture>
#include <metal_matrix>
using namespace metal; using namespace metal;
struct SolidVertex struct SolidVertexOutput
{ {
float4 position [[position]]; float4 position [[position]];
float pointSize [[point_size]]; float pointSize [[point_size]];
}; };
vertex SolidVertex SDL_Solid_vertex(constant float2 *position [[buffer(0)]], uint vid [[vertex_id]]) vertex SolidVertexOutput SDL_Solid_vertex(const device float2 *position [[buffer(0)]],
constant float4x4 &projection [[buffer(2)]],
uint vid [[vertex_id]])
{ {
SolidVertex v; SolidVertexOutput v;
v.position = float4(position[vid].x, position[vid].y, 0.0f, 1.0f); v.position = projection * float4(position[vid].x, position[vid].y, 0.0f, 1.0f);
v.pointSize = 0.5f; v.pointSize = 0.5f;
return v; return v;
} }
@ -21,27 +24,34 @@ fragment float4 SDL_Solid_fragment(constant float4 &col [[buffer(0)]])
return col; return col;
} }
struct CopyVertex struct CopyVertexOutput
{ {
float4 position [[position]]; float4 position [[position]];
float2 texcoord; float2 texcoord;
}; };
vertex CopyVertex SDL_Copy_vertex(constant float2 *position [[buffer(0)]], constant float2 *texcoords [[buffer(1)]], uint vid [[vertex_id]]) vertex CopyVertexOutput SDL_Copy_vertex(const device float2 *position [[buffer(0)]],
const device float2 *texcoords [[buffer(1)]],
constant float4x4 &projection [[buffer(2)]],
uint vid [[vertex_id]])
{ {
CopyVertex v; CopyVertexOutput v;
v.position = float4(position[vid].x, position[vid].y, 0.0f, 1.0f); v.position = projection * float4(position[vid].x, position[vid].y, 0.0f, 1.0f);
v.texcoord = texcoords[vid]; v.texcoord = texcoords[vid];
return v; return v;
} }
fragment float4 SDL_Copy_fragment_nearest(CopyVertex vert [[stage_in]], constant float4 &col [[buffer(0)]], texture2d<float> tex [[texture(0)]]) fragment float4 SDL_Copy_fragment_nearest(CopyVertexOutput vert [[stage_in]],
constant float4 &col [[buffer(0)]],
texture2d<float> tex [[texture(0)]])
{ {
constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::nearest); constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::nearest);
return tex.sample(s, vert.texcoord) * col; return tex.sample(s, vert.texcoord) * col;
} }
fragment float4 SDL_Copy_fragment_linear(CopyVertex vert [[stage_in]], constant float4 &col [[buffer(0)]], texture2d<float> tex [[texture(0)]]) fragment float4 SDL_Copy_fragment_linear(CopyVertexOutput vert [[stage_in]],
constant float4 &col [[buffer(0)]],
texture2d<float> tex [[texture(0)]])
{ {
constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::linear); constexpr sampler s(coord::normalized, address::clamp_to_edge, filter::linear);
return tex.sample(s, vert.texcoord) * col; return tex.sample(s, vert.texcoord) * col;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff