From 1e77dae7b7a0502bbac1fd438c9752a1dbdd8108 Mon Sep 17 00:00:00 2001
From: Sylvain Becker <[EMAIL REDACTED]>
Date: Tue, 16 Mar 2021 15:44:04 +0100
Subject: [PATCH] Add METAL implementation
---
src/render/metal/SDL_render_metal.m | 186 +++++++++++++++++++++++++---
1 file changed, 171 insertions(+), 15 deletions(-)
diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m
index 67ac1b663f..6de16849b8 100644
--- a/src/render/metal/SDL_render_metal.m
+++ b/src/render/metal/SDL_render_metal.m
@@ -208,6 +208,8 @@ - (void)dealloc
static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
+static const float inv255f = 1.0f / 255.0f;
+
static MTLBlendOperation
GetBlendOperation(SDL_BlendOperation operation)
{
@@ -279,26 +281,35 @@ - (void)dealloc
switch (cache->vertexFunction) {
case SDL_METAL_VERTEX_SOLID:
- /* position (float2) */
- vertdesc.layouts[0].stride = sizeof(float) * 2;
+ /* position (float2), color (float4) */
+ vertdesc.layouts[0].stride = sizeof(float) * (2 + 4);
vertdesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
vertdesc.attributes[0].format = MTLVertexFormatFloat2;
vertdesc.attributes[0].offset = 0;
vertdesc.attributes[0].bufferIndex = 0;
+
+ vertdesc.attributes[1].format = MTLVertexFormatFloat4;
+ vertdesc.attributes[1].offset = sizeof (float) * 2;
+ vertdesc.attributes[1].bufferIndex = 0;
+
break;
case SDL_METAL_VERTEX_COPY:
- /* position (float2), texcoord (float2) */
- vertdesc.layouts[0].stride = sizeof(float) * 4;
+ /* position (float2), color (float4), texcoord (float2) */
+ vertdesc.layouts[0].stride = sizeof(float) * (2 + 4 + 2);
vertdesc.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
vertdesc.attributes[0].format = MTLVertexFormatFloat2;
vertdesc.attributes[0].offset = 0;
vertdesc.attributes[0].bufferIndex = 0;
- vertdesc.attributes[1].format = MTLVertexFormatFloat2;
- vertdesc.attributes[1].offset = sizeof(float) * 2;
+ vertdesc.attributes[1].format = MTLVertexFormatFloat4;
+ vertdesc.attributes[1].offset = sizeof (float) * 2;
vertdesc.attributes[1].bufferIndex = 0;
+
+ vertdesc.attributes[2].format = MTLVertexFormatFloat2;
+ vertdesc.attributes[2].offset = sizeof(float) * (2 + 4);
+ vertdesc.attributes[2].bufferIndex = 0;
break;
}
@@ -1060,38 +1071,67 @@ - (void)dealloc
if (!verts) {
return -1;
}
+ /*
+ * FIXME: not needed anymore, some cleanup to do
+ *
*(verts++) = ((float)cmd->data.color.r) / 255.0f;
*(verts++) = ((float)cmd->data.color.g) / 255.0f;
*(verts++) = ((float)cmd->data.color.b) / 255.0f;
*(verts++) = ((float)cmd->data.color.a) / 255.0f;
+ */
return 0;
}
static int
METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
{
- const size_t vertlen = (sizeof (float) * 2) * count;
+ const float r = cmd->data.draw.r * inv255f;
+ const float g = cmd->data.draw.g * inv255f;
+ const float b = cmd->data.draw.b * inv255f;
+ const float a = cmd->data.draw.a * inv255f;
+ const size_t vertlen = sizeof (float) * (2 + 4) * count;
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
if (!verts) {
return -1;
}
cmd->data.draw.count = count;
- SDL_memcpy(verts, points, vertlen);
+
+ for (int i = 0; i < count; i++, points++) {
+ *(verts++) = points->x;
+ *(verts++) = points->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
+ }
return 0;
}
static int
METAL_QueueDrawLines(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
{
+ const float r = cmd->data.draw.r * inv255f;
+ const float g = cmd->data.draw.g * inv255f;
+ const float b = cmd->data.draw.b * inv255f;
+ const float a = cmd->data.draw.a * inv255f;
+
SDL_assert(count >= 2); /* should have been checked at the higher level. */
- const size_t vertlen = (sizeof (float) * 2) * count;
+ const size_t vertlen = sizeof (float) * (2 + 4) * count;
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
if (!verts) {
return -1;
}
cmd->data.draw.count = count;
- SDL_memcpy(verts, points, vertlen);
+
+ for (int i = 0; i < count; i++, points++) {
+ *(verts++) = points->x;
+ *(verts++) = points->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
+ }
/* If the line segment is completely horizontal or vertical,
make it one pixel longer, to satisfy the diamond-exit rule.
@@ -1101,8 +1141,8 @@ - (void)dealloc
that are missing a pixel that frames something and not arbitrary
angles. Maybe !!! FIXME for later, though. */
- points += count - 2; /* update the last line. */
- verts += (count * 2) - 2;
+ points -= 2; /* update the last line. */
+ verts -= (count * 2) - 2;
const float xstart = points[0].x;
const float ystart = points[0].y;
@@ -1121,7 +1161,11 @@ - (void)dealloc
static int
METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
{
- const size_t vertlen = (sizeof (float) * 8) * count;
+ const float r = cmd->data.draw.r * inv255f;
+ const float g = cmd->data.draw.g * inv255f;
+ const float b = cmd->data.draw.b * inv255f;
+ const float a = cmd->data.draw.a * inv255f;
+ const size_t vertlen = sizeof (float) * 4 * (2 + 4) * count;
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
if (!verts) {
return -1;
@@ -1140,12 +1184,31 @@ - (void)dealloc
} else {
*(verts++) = rects->x;
*(verts++) = rects->y + rects->h;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
+
*(verts++) = rects->x;
*(verts++) = rects->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
+
*(verts++) = rects->x + rects->w;
*(verts++) = rects->y + rects->h;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
+
*(verts++) = rects->x + rects->w;
*(verts++) = rects->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
}
}
@@ -1162,8 +1225,13 @@ - (void)dealloc
{
const float texw = (float) texture->w;
const float texh = (float) texture->h;
+ const float r = cmd->data.draw.r * inv255f;
+ const float g = cmd->data.draw.g * inv255f;
+ const float b = cmd->data.draw.b * inv255f;
+ const float a = cmd->data.draw.a * inv255f;
+
// !!! FIXME: use an index buffer
- const size_t vertlen = (sizeof (float) * 16);
+ const size_t vertlen = (sizeof (float) * 4 * (2 + 4 + 2));
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
if (!verts) {
return -1;
@@ -1174,21 +1242,37 @@ - (void)dealloc
/* Interleaved positions and texture coordinates */
*(verts++) = dstrect->x;
*(verts++) = dstrect->y + dstrect->h;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = normtex(srcrect->x, texw);
*(verts++) = normtex(srcrect->y + srcrect->h, texh);
*(verts++) = dstrect->x;
*(verts++) = dstrect->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = normtex(srcrect->x, texw);
*(verts++) = normtex(srcrect->y, texh);
*(verts++) = dstrect->x + dstrect->w;
*(verts++) = dstrect->y + dstrect->h;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = normtex(srcrect->x + srcrect->w, texw);
*(verts++) = normtex(srcrect->y + srcrect->h, texh);
*(verts++) = dstrect->x + dstrect->w;
*(verts++) = dstrect->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = normtex(srcrect->x + srcrect->w, texw);
*(verts++) = normtex(srcrect->y, texh);
@@ -1200,12 +1284,16 @@ - (void)dealloc
const SDL_Rect * srcquad, const SDL_FRect * dstrect,
const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
{
+ const float r = cmd->data.draw.r * inv255f;
+ const float g = cmd->data.draw.g * inv255f;
+ const float b = cmd->data.draw.b * inv255f;
+ const float a = cmd->data.draw.a * inv255f;
const float texw = (float) texture->w;
const float texh = (float) texture->h;
const float rads = (float)(M_PI * (float) angle / 180.0f);
const float c = cosf(rads), s = sinf(rads);
float minu, maxu, minv, maxv;
- const size_t vertlen = (sizeof (float) * 32);
+ const size_t vertlen = sizeof (float) * (16 + 4 * (2 + 4 + 2));
float *verts;
// cheat and store this offset in (count) because it needs to be aligned in ways other fields don't and we aren't using count otherwise.
@@ -1252,27 +1340,80 @@ - (void)dealloc
/* Interleaved positions and texture coordinates */
*(verts++) = -center->x;
*(verts++) = dstrect->h - center->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = minu;
*(verts++) = maxv;
*(verts++) = -center->x;
*(verts++) = -center->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = minu;
*(verts++) = minv;
*(verts++) = dstrect->w - center->x;
*(verts++) = dstrect->h - center->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = maxu;
*(verts++) = maxv;
*(verts++) = dstrect->w - center->x;
*(verts++) = -center->y;
+ *(verts++) = r;
+ *(verts++) = g;
+ *(verts++) = b;
+ *(verts++) = a;
*(verts++) = maxu;
*(verts++) = minv;
return 0;
}
+static int
+METAL_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
+ SDL_Vertex *vertices, int num_vertices, int *indices, int num_indices, float scale_x, float scale_y)
+{
+ const float texw = (float) (texture ? texture->w : 0);
+ const float texh = (float) (texture ? texture->h : 0);
+ int count = indices ? num_indices : num_vertices;
+ int i;
+ int sz = 2 + 4 + (texture ? 2 : 0);
+
+ const size_t vertlen = sizeof (float) * sz * count;
+ float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first);
+ if (!verts) {
+ return -1;
+ }
+
+ cmd->data.draw.count = count;
+
+ for (i = 0; i < count; i++) {
+ SDL_Vertex *v = &vertices[indices ? indices[i] : i];
+
+ *(verts++) = v->position.x * scale_x;
+ *(verts++) = v->position.y * scale_y;
+
+ *(verts++) = v->color.r * inv255f;
+ *(verts++) = v->color.g * inv255f;
+ *(verts++) = v->color.b * inv255f;
+ *(verts++) = v->color.a * inv255f;
+
+ if (texture) {
+ *(verts++) = normtex(v->tex_coord.x, texw);
+ *(verts++) = normtex(v->tex_coord.y, texh);
+ }
+ }
+
+ return 0;
+}
typedef struct
{
@@ -1532,6 +1673,20 @@ - (void)dealloc
break;
}
+ case SDL_RENDERCMD_GEOMETRY: {
+ const size_t count = cmd->data.draw.count;
+ SDL_Texture *texture = cmd->data.draw.texture;
+
+ if (texture) {
+ SetCopyState(renderer, cmd, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
+ [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:count];
+ } else {
+ SetDrawState(renderer, cmd, SDL_METAL_FRAGMENT_SOLID, CONSTANTS_OFFSET_IDENTITY, mtlbufvertex, &statecache);
+ [data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:count];
+ }
+ break;
+ }
+
case SDL_RENDERCMD_NO_OP:
break;
}
@@ -1894,6 +2049,7 @@ - (void)dealloc
renderer->QueueFillRects = METAL_QueueFillRects;
renderer->QueueCopy = METAL_QueueCopy;
renderer->QueueCopyEx = METAL_QueueCopyEx;
+ renderer->QueueGeometry = METAL_QueueGeometry;
renderer->RunCommandQueue = METAL_RunCommandQueue;
renderer->RenderReadPixels = METAL_RenderReadPixels;
renderer->RenderPresent = METAL_RenderPresent;