Skip to content
Permalink
Browse files

metal: use a projection matrix instead of manually transforming verti…

…ces into clip space on the CPU.
  • Loading branch information
slime73 committed Dec 31, 2017
1 parent 49df65c commit e24dc9053bb72a7b6aa5d59e4be75e6baabb2dce
@@ -324,7 +324,7 @@ @implementation METAL_TextureData
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 @@ static void METAL_ActivateRenderer(SDL_Renderer * renderer)
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 @@ static void METAL_ActivateRenderer(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 @@ static void METAL_ActivateRenderer(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 @@ static void METAL_ActivateRenderer(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,34 +631,16 @@ static void METAL_ActivateRenderer(SDL_Renderer * renderer)
{ @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.
const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };

[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 @@ static void METAL_ActivateRenderer(SDL_Renderer * renderer)
[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 @@ static void METAL_ActivateRenderer(SDL_Renderer * renderer)
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[] = {
@@ -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;

0 comments on commit e24dc90

Please sign in to comment.