Browse Source

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

Alex Szpakowski 7 years ago
parent
commit
e24dc9053b

+ 47 - 50
src/render/metal/SDL_render_metal.m

@@ -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[] = {

+ 20 - 10
src/render/metal/SDL_shaders_metal.metal

@@ -1,17 +1,20 @@
 #include <metal_texture>
+#include <metal_matrix>
 
 using namespace metal;
 
-struct SolidVertex
+struct SolidVertexOutput
 {
 	float4 position [[position]];
 	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;
-	v.position = float4(position[vid].x, position[vid].y, 0.0f, 1.0f);
+    SolidVertexOutput v;
+	v.position = projection * float4(position[vid].x, position[vid].y, 0.0f, 1.0f);
 	v.pointSize = 0.5f;
 	return v;
 }
@@ -21,27 +24,34 @@ fragment float4 SDL_Solid_fragment(constant float4 &col [[buffer(0)]])
     return col;
 }
 
-struct CopyVertex
+struct CopyVertexOutput
 {
     float4 position [[position]];
     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;
-    v.position = float4(position[vid].x, position[vid].y, 0.0f, 1.0f);
+    CopyVertexOutput v;
+    v.position = projection * float4(position[vid].x, position[vid].y, 0.0f, 1.0f);
     v.texcoord = texcoords[vid];
     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);
     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);
     return tex.sample(s, vert.texcoord) * col;

File diff suppressed because it is too large
+ 289 - 659
src/render/metal/SDL_shaders_metal_ios.h


File diff suppressed because it is too large
+ 486 - 666
src/render/metal/SDL_shaders_metal_osx.h


Some files were not shown because too many files changed in this diff