mirror of
				https://github.com/libsdl-org/SDL.git
				synced 2025-11-04 01:34:38 +00:00 
			
		
		
		
	metal: use a projection matrix instead of manually transforming vertices into clip space on the CPU.
This commit is contained in:
		@@ -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[] = {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user