SDL: Refactor render state

From 2209b7178988f059b6039de3d870bd76dbcb0874 Mon Sep 17 00:00:00 2001
From: Ivan Epifanov <[EMAIL REDACTED]>
Date: Tue, 24 Nov 2020 18:12:38 +0300
Subject: [PATCH] Refactor render state

---
 src/render/vitagxm/SDL_render_vita_gxm.c      | 317 ++++++++++--------
 .../vitagxm/SDL_render_vita_gxm_tools.c       |   3 +-
 .../vitagxm/SDL_render_vita_gxm_tools.h       |   3 +
 .../vitagxm/SDL_render_vita_gxm_types.h       |   9 +
 4 files changed, 186 insertions(+), 146 deletions(-)

diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c
index 925df80eb..866bb5c53 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm.c
+++ b/src/render/vitagxm/SDL_render_vita_gxm.c
@@ -357,9 +357,8 @@ VITA_GXM_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
 }
 
 static void
-VITA_GXM_SetBlendMode(SDL_Renderer *renderer, int blendMode)
+VITA_GXM_SetBlendMode(VITA_GXM_RenderData *data, int blendMode)
 {
-    VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
     if (blendMode != data->currentBlendMode)
     {
         fragment_programs *in = &data->blendFragmentPrograms.blend_mode_blend;
@@ -534,16 +533,6 @@ VITA_GXM_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture
 
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    Uint8 r, g, b, a;
-    SDL_GetTextureColorMod(texture, &r, &g, &b);
-    SDL_GetTextureAlphaMod(texture, &a);
-
-    cmd->data.draw.r = r;
-    cmd->data.draw.g = g;
-    cmd->data.draw.b = b;
-    cmd->data.draw.a = a;
-    cmd->data.draw.blend = renderer->blendMode;
-
     cmd->data.draw.count = 1;
 
     texture_vertex *vertices = (texture_vertex *)pool_memalign(
@@ -593,16 +582,6 @@ VITA_GXM_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Textur
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    Uint8 r, g, b, a;
-    SDL_GetTextureColorMod(texture, &r, &g, &b);
-    SDL_GetTextureAlphaMod(texture, &a);
-
-    cmd->data.draw.r = r;
-    cmd->data.draw.g = g;
-    cmd->data.draw.b = b;
-    cmd->data.draw.a = a;
-    cmd->data.draw.blend = renderer->blendMode;
-
     cmd->data.draw.count = 1;
 
     texture_vertex *vertices = (texture_vertex *)pool_memalign(
@@ -705,22 +684,6 @@ VITA_GXM_RenderDrawPoints(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    if (data->drawstate.fragment_program != data->colorFragmentProgram || data->drawstate.vertex_program != data->colorVertexProgram) {
-        data->drawstate.fragment_program = data->colorFragmentProgram;
-        data->drawstate.vertex_program = data->colorVertexProgram;
-
-        sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
-        sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
-
-
-        void *vertexDefaultBuffer;
-        sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertexDefaultBuffer);
-        sceGxmSetUniformDataF(vertexDefaultBuffer, data->colorWvpParam, 0, 16, data->ortho_matrix);
-    }
-
-
-    sceGxmSetVertexStream(data->gxm_context, 0, (const void*)cmd->data.draw.first);
-
     sceGxmSetFrontPolygonMode(data->gxm_context, SCE_GXM_POLYGON_MODE_POINT);
     sceGxmDraw(data->gxm_context, SCE_GXM_PRIMITIVE_POINTS, SCE_GXM_INDEX_FORMAT_U16, data->linearIndices, cmd->data.draw.count);
     sceGxmSetFrontPolygonMode(data->gxm_context, SCE_GXM_POLYGON_MODE_TRIANGLE_FILL);
@@ -733,19 +696,6 @@ VITA_GXM_RenderDrawLines(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    if (data->drawstate.fragment_program != data->colorFragmentProgram || data->drawstate.vertex_program != data->colorVertexProgram) {
-        data->drawstate.fragment_program = data->colorFragmentProgram;
-        data->drawstate.vertex_program = data->colorVertexProgram;
-
-        sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
-        sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
-
-
-        void *vertexDefaultBuffer;
-        sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertexDefaultBuffer);
-        sceGxmSetUniformDataF(vertexDefaultBuffer, data->colorWvpParam, 0, 16, data->ortho_matrix);
-    }
-
     sceGxmSetFrontPolygonMode(data->gxm_context, SCE_GXM_POLYGON_MODE_LINE);
     sceGxmDraw(data->gxm_context, SCE_GXM_PRIMITIVE_LINES, SCE_GXM_INDEX_FORMAT_U16, data->linearIndices, cmd->data.draw.count);
     sceGxmSetFrontPolygonMode(data->gxm_context, SCE_GXM_POLYGON_MODE_TRIANGLE_FILL);
@@ -758,26 +708,164 @@ VITA_GXM_RenderFillRects(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    if (data->drawstate.fragment_program != data->colorFragmentProgram || data->drawstate.vertex_program != data->colorVertexProgram) {
-        data->drawstate.fragment_program = data->colorFragmentProgram;
-        data->drawstate.vertex_program = data->colorVertexProgram;
+    sceGxmSetVertexStream(data->gxm_context, 0, (const void*)cmd->data.draw.first);
+    sceGxmDraw(data->gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, data->linearIndices, 4 * cmd->data.draw.count);
+
+    return 0;
+}
+
+
+static int
+SetDrawState(VITA_GXM_RenderData *data, const SDL_RenderCommand *cmd, SDL_bool solid)
+{
+    SDL_Texture *texture = cmd->data.draw.texture;
+    const SDL_BlendMode blend = cmd->data.draw.blend;
+    SceGxmFragmentProgram *fragment_program;
+    SceGxmVertexProgram *vertex_program;
+    SDL_bool matrix_updated = SDL_FALSE;
+    SDL_bool program_updated = SDL_FALSE;
+
+    Uint8 r, g, b, a;
+    r = cmd->data.draw.r;
+    g = cmd->data.draw.g;
+    b = cmd->data.draw.b;
+    a = cmd->data.draw.a;
+
+    if (data->drawstate.viewport_dirty) {
+        // TODO: this isn't right
+        /*
+        const SDL_Rect *viewport = &data->drawstate.viewport;
+
+        float x_scale = (int)(viewport->w) >> 1;
+        float x_off = viewport->x + x_scale;
+        float y_scale = -((int)(viewport->h) >> 1);
+        float y_off = -1.0 * (viewport->y + y_scale);
+
+        sceGxmSetViewport(data->gxm_context, x_off, x_scale, y_off, y_scale, 0.f, 1.f);
+
+        if (viewport->w && viewport->h) {
+            init_orthographic_matrix(data->ortho_matrix, viewport->x, viewport->x + viewport->w, viewport->y + viewport->h, viewport->y, 0.0f, 1.0f);
+            matrix_updated = SDL_TRUE;
+        }
+        */
+        data->drawstate.viewport_dirty = SDL_FALSE;
+    }
+
+    if (data->drawstate.cliprect_enabled_dirty) {
+        if (!data->drawstate.cliprect_enabled) {
+            unset_clip_rectangle(data);
+        }
+        data->drawstate.cliprect_enabled_dirty = SDL_FALSE;
+    }
 
-        sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
-        sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
+    if (data->drawstate.cliprect_enabled && data->drawstate.cliprect_dirty) {
+        const SDL_Rect *viewport = &data->drawstate.viewport;
+        const SDL_Rect *rect = &data->drawstate.cliprect;
+        set_clip_rectangle(data, viewport->x + rect->x,
+                        data->drawstate.target ? viewport->y + rect->y : data->drawstate.drawableh - viewport->y - rect->y - rect->h,
+                        rect->w, rect->h);
+        data->drawstate.cliprect_dirty = SDL_FALSE;
+    }
 
+    VITA_GXM_SetBlendMode(data, blend); // do that first, to select appropriate shaders
 
-        void *vertexDefaultBuffer;
-        sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertexDefaultBuffer);
-        sceGxmSetUniformDataF(vertexDefaultBuffer, data->colorWvpParam, 0, 16, data->ortho_matrix);
+    if (texture) {
+        vertex_program = data->textureVertexProgram;
+        if(cmd->data.draw.r == 255 && cmd->data.draw.g == 255 && cmd->data.draw.b == 255 && cmd->data.draw.a == 255) {
+            fragment_program = data->textureFragmentProgram;
+        } else {
+            fragment_program = data->textureTintFragmentProgram;
+        }
+    } else {
+        vertex_program = data->colorVertexProgram;
+        fragment_program = data->colorFragmentProgram;
     }
 
+    if (data->drawstate.vertex_program != vertex_program) {
+        data->drawstate.vertex_program = vertex_program;
+        sceGxmSetVertexProgram(data->gxm_context, vertex_program);
+        program_updated = SDL_TRUE;
+    }
+
+    if (data->drawstate.fragment_program != fragment_program) {
+        data->drawstate.fragment_program = fragment_program;
+        sceGxmSetFragmentProgram(data->gxm_context, fragment_program);
+        program_updated = SDL_TRUE;
+    }
+
+    Uint32 texture_color = ((a << 24) | (b << 16) | (g << 8) | r);
+
+    if (program_updated || matrix_updated) {
+        if (data->drawstate.fragment_program == data->textureFragmentProgram) {
+            void *vertex_wvp_buffer;
+            sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertex_wvp_buffer);
+            sceGxmSetUniformDataF(vertex_wvp_buffer, data->textureWvpParam, 0, 16, data->ortho_matrix);
+        } else if (data->drawstate.fragment_program == data->textureTintFragmentProgram) {
+            void *vertex_wvp_buffer;
+            sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertex_wvp_buffer);
+            sceGxmSetUniformDataF(vertex_wvp_buffer, data->textureWvpParam, 0, 16, data->ortho_matrix);
+
+            void *texture_tint_color_buffer;
+            sceGxmReserveFragmentDefaultUniformBuffer(data->gxm_context, &texture_tint_color_buffer);
+
+            float *tint_color = pool_memalign(
+                data,
+                4 * sizeof(float), // RGBA
+                sizeof(float)
+            );
+
+            tint_color[0] = r / 255.0f;
+            tint_color[1] = g / 255.0f;
+            tint_color[2] = b / 255.0f;
+            tint_color[3] = a / 255.0f;
+            sceGxmSetUniformDataF(texture_tint_color_buffer, data->textureTintColorParam, 0, 4, tint_color);
+            data->drawstate.texture_color = texture_color;
+        } else { // color
+            void *vertexDefaultBuffer;
+            sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertexDefaultBuffer);
+            sceGxmSetUniformDataF(vertexDefaultBuffer, data->colorWvpParam, 0, 16, data->ortho_matrix);
+        }
+    } else {
+        if (data->drawstate.fragment_program == data->textureTintFragmentProgram && data->drawstate.texture_color != texture_color) {
+            void *texture_tint_color_buffer;
+            sceGxmReserveFragmentDefaultUniformBuffer(data->gxm_context, &texture_tint_color_buffer);
+
+            float *tint_color = pool_memalign(
+                data,
+                4 * sizeof(float), // RGBA
+                sizeof(float)
+            );
+
+            tint_color[0] = r / 255.0f;
+            tint_color[1] = g / 255.0f;
+            tint_color[2] = b / 255.0f;
+            tint_color[3] = a / 255.0f;
+            sceGxmSetUniformDataF(texture_tint_color_buffer, data->textureTintColorParam, 0, 4, tint_color);
+            data->drawstate.texture_color = texture_color;
+        }
+    }
+
+    if (texture != data->drawstate.texture) {
+        if (texture) {
+            VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *) cmd->data.draw.texture->driverdata;
+            sceGxmSetFragmentTexture(data->gxm_context, 0, &vita_texture->tex->gxm_tex);
+        }
+        data->drawstate.texture = texture;
+    }
+
+    /* all drawing commands use this */
     sceGxmSetVertexStream(data->gxm_context, 0, (const void*)cmd->data.draw.first);
-    sceGxmDraw(data->gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, data->linearIndices, 4 * cmd->data.draw.count);
 
     return 0;
 }
 
+static int
+SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
+{
+    VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
+    return SetDrawState(data, cmd, SDL_FALSE);
+}
 
 static int
 VITA_GXM_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
@@ -785,126 +873,67 @@ VITA_GXM_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *
     StartDrawing(renderer);
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
+    data->drawstate.target = renderer->target;
+    if (!data->drawstate.target) {
+        SDL_GL_GetDrawableSize(renderer->window, &data->drawstate.drawablew, &data->drawstate.drawableh);
+    }
+
     while (cmd) {
         switch (cmd->command) {
-            case SDL_RENDERCMD_SETDRAWCOLOR: {
-                break;
-            }
 
             case SDL_RENDERCMD_SETVIEWPORT: {
-                // TODO
+                SDL_Rect *viewport = &data->drawstate.viewport;
+                if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) {
+                    SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect));
+                    data->drawstate.viewport_dirty = SDL_TRUE;
+                }
                 break;
             }
 
             case SDL_RENDERCMD_SETCLIPRECT: {
                 const SDL_Rect *rect = &cmd->data.cliprect.rect;
-                if (cmd->data.cliprect.enabled)
-                {
-                    set_clip_rectangle(data, rect->x, rect->y, rect->w, rect->h);
+                if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) {
+                    data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled;
+                    data->drawstate.cliprect_enabled_dirty = SDL_TRUE;
                 }
-                else
-                {
-                    unset_clip_rectangle(data);
+
+                if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) {
+                    SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect));
+                    data->drawstate.cliprect_dirty = SDL_TRUE;
                 }
                 break;
             }
 
+            case SDL_RENDERCMD_SETDRAWCOLOR: {
+                break;
+            }
+
             case SDL_RENDERCMD_CLEAR: {
                 VITA_GXM_RenderClear(renderer, cmd);
                 break;
             }
 
             case SDL_RENDERCMD_DRAW_POINTS: {
-                VITA_GXM_SetBlendMode(renderer, cmd->data.draw.blend);
+                SetDrawState(data, cmd, SDL_FALSE);
                 VITA_GXM_RenderDrawPoints(renderer, cmd);
                 break;
             }
 
             case SDL_RENDERCMD_DRAW_LINES: {
-                VITA_GXM_SetBlendMode(renderer, cmd->data.draw.blend);
+                SetDrawState(data, cmd, SDL_FALSE);
                 VITA_GXM_RenderDrawLines(renderer, cmd);
                 break;
             }
 
             case SDL_RENDERCMD_FILL_RECTS: {
-                VITA_GXM_SetBlendMode(renderer, cmd->data.draw.blend);
+                SetDrawState(data, cmd, SDL_FALSE);
                 VITA_GXM_RenderFillRects(renderer, cmd);
                 break;
             }
 
             case SDL_RENDERCMD_COPY:
             case SDL_RENDERCMD_COPY_EX: {
-                SDL_BlendMode blend;
-                SDL_GetTextureBlendMode(cmd->data.draw.texture, &blend);
-                VITA_GXM_SetBlendMode(renderer, blend);
-
-                Uint8 r, g, b, a;
-                r = cmd->data.draw.r;
-                g = cmd->data.draw.g;
-                b = cmd->data.draw.b;
-                a = cmd->data.draw.a;
-
-                if (data->drawstate.vertex_program != data->textureVertexProgram) {
-                    data->drawstate.vertex_program = data->textureVertexProgram;
-                    sceGxmSetVertexProgram(data->gxm_context, data->textureVertexProgram);
-                }
-
-                if(r == 255 && g == 255 && b == 255 && a == 255)
-                {
-                    if (data->drawstate.fragment_program != data->textureFragmentProgram) {
-                        data->drawstate.fragment_program = data->textureFragmentProgram;
-                        sceGxmSetFragmentProgram(data->gxm_context, data->textureFragmentProgram);
-                    }
-                }
-                else
-                {
-                    if (data->drawstate.fragment_program != data->textureTintFragmentProgram) {
-                        data->drawstate.fragment_program = data->textureTintFragmentProgram;
-                        sceGxmSetFragmentProgram(data->gxm_context, data->textureTintFragmentProgram);
-                    }
-
-                    Uint32 texture_color = ((a << 24) | (b << 16) | (g << 8) | r);
-
-                    if (
-                        (data->drawstate.last_command != SDL_RENDERCMD_COPY && data->drawstate.last_command != SDL_RENDERCMD_COPY_EX)
-                        || data->drawstate.texture_color != texture_color
-                    ) {
-                        data->drawstate.texture_color = texture_color;
-                        void *texture_tint_color_buffer;
-                        sceGxmReserveFragmentDefaultUniformBuffer(data->gxm_context, &texture_tint_color_buffer);
-
-                        float *tint_color = pool_memalign(
-                            data,
-                            4 * sizeof(float), // RGBA
-                            sizeof(float)
-                        );
-
-                        tint_color[0] = r / 255.0f;
-                        tint_color[1] = g / 255.0f;
-                        tint_color[2] = b / 255.0f;
-                        tint_color[3] = a / 255.0f;
-
-                        sceGxmSetUniformDataF(texture_tint_color_buffer, data->textureTintColorParam, 0, 4, tint_color);
-                    }
-
-                }
-
-                if (data->drawstate.last_command != SDL_RENDERCMD_COPY && data->drawstate.last_command != SDL_RENDERCMD_COPY_EX) {
-                    void *vertex_wvp_buffer;
-                    sceGxmReserveVertexDefaultUniformBuffer(data->gxm_context, &vertex_wvp_buffer);
-                    sceGxmSetUniformDataF(vertex_wvp_buffer, data->textureWvpParam, 0, 16, data->ortho_matrix);
-                }
-
-                if (data->drawstate.texture != cmd->data.draw.texture) {
-                    data->drawstate.texture = cmd->data.draw.texture;
-                    // TODO: check if texture changed
-                    VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *) cmd->data.draw.texture->driverdata;
-
-                    sceGxmSetFragmentTexture(data->gxm_context, 0, &vita_texture->tex->gxm_tex);
-                }
-
-
-                sceGxmSetVertexStream(data->gxm_context, 0, (const void*)cmd->data.draw.first);
+                SetCopyState(renderer, cmd);
                 sceGxmDraw(data->gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, data->linearIndices, 4 * cmd->data.draw.count);
 
                 break;
diff --git a/src/render/vitagxm/SDL_render_vita_gxm_tools.c b/src/render/vitagxm/SDL_render_vita_gxm_tools.c
index 757f1f495..f410595cb 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm_tools.c
+++ b/src/render/vitagxm/SDL_render_vita_gxm_tools.c
@@ -44,7 +44,7 @@
 #include "SDL_render_vita_gxm_memory.h"
 #include "SDL_render_vita_gxm_shaders.h"
 
-static void
+void
 init_orthographic_matrix(float *m, float left, float right, float bottom, float top, float near, float far)
 {
     m[0x0] = 2.0f/(right-left);
@@ -68,7 +68,6 @@ init_orthographic_matrix(float *m, float left, float right, float bottom, float
     m[0xF] = 1.0f;
 }
 
-
 static void *
 patcher_host_alloc(void *user_data, unsigned int size)
 {
diff --git a/src/render/vitagxm/SDL_render_vita_gxm_tools.h b/src/render/vitagxm/SDL_render_vita_gxm_tools.h
index 5787e4d3a..c1dfc6c0e 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm_tools.h
+++ b/src/render/vitagxm/SDL_render_vita_gxm_tools.h
@@ -36,6 +36,9 @@
 
 #include "SDL_render_vita_gxm_types.h"
 
+void
+init_orthographic_matrix(float *m, float left, float right, float bottom, float top, float near, float far);
+
 void *pool_malloc(VITA_GXM_RenderData *data, unsigned int size);
 void *pool_memalign(VITA_GXM_RenderData *data, unsigned int size, unsigned int alignment);
 
diff --git a/src/render/vitagxm/SDL_render_vita_gxm_types.h b/src/render/vitagxm/SDL_render_vita_gxm_types.h
index af4c3316d..64a323e35 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm_types.h
+++ b/src/render/vitagxm/SDL_render_vita_gxm_types.h
@@ -108,6 +108,15 @@ typedef struct
     SceGxmFragmentProgram *fragment_program;
     SceGxmVertexProgram *vertex_program;
     int last_command;
+
+    SDL_bool cliprect_enabled_dirty;
+    SDL_bool cliprect_enabled;
+    SDL_bool cliprect_dirty;
+    SDL_Rect cliprect;
+    SDL_bool texturing;
+    Uint32 clear_color;
+    int drawablew;
+    int drawableh;
 } gxm_drawstate_cache;
 
 typedef struct