SDL: Add drawstate cache

From 333d5e11e1c7677a45c6fb639f4ae0f5eeac4935 Mon Sep 17 00:00:00 2001
From: Ivan Epifanov <[EMAIL REDACTED]>
Date: Mon, 23 Nov 2020 20:15:39 +0300
Subject: [PATCH] Add drawstate cache

---
 src/render/vitagxm/SDL_render_vita_gxm.c      | 128 ++++++++++++------
 .../vitagxm/SDL_render_vita_gxm_tools.c       |   2 +
 .../vitagxm/SDL_render_vita_gxm_types.h       |   4 +
 3 files changed, 94 insertions(+), 40 deletions(-)

diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c
index dc76d4f83..925df80eb 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm.c
+++ b/src/render/vitagxm/SDL_render_vita_gxm.c
@@ -126,6 +126,12 @@ StartDrawing(SDL_Renderer *renderer)
         return;
     }
 
+    data->drawstate.texture = NULL;
+    data->drawstate.vertex_program = NULL;
+    data->drawstate.fragment_program = NULL;
+    data->drawstate.last_command = -1;
+    data->drawstate.texture_color = 0xFFFFFFFF;
+
     // reset blend mode
 //    data->currentBlendMode = SDL_BLENDMODE_BLEND;
 //    fragment_programs *in = &data->blendFragmentPrograms.blend_mode_blend;
@@ -676,6 +682,8 @@ VITA_GXM_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
     clear_color[3] = (cmd->data.color.a)/255.0f;
 
     // set clear shaders
+    data->drawstate.fragment_program = data->clearFragmentProgram;
+    data->drawstate.vertex_program = data->clearVertexProgram;
     sceGxmSetVertexProgram(data->gxm_context, data->clearVertexProgram);
     sceGxmSetFragmentProgram(data->gxm_context, data->clearFragmentProgram);
 
@@ -697,12 +705,19 @@ VITA_GXM_RenderDrawPoints(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
-    sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
+    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);
+    }
 
-    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);
 
@@ -718,16 +733,18 @@ VITA_GXM_RenderDrawLines(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
-    sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
+    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;
 
-    void *vertexDefaultBuffer;
+        sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
+        sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
 
-    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);
+        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);
@@ -741,12 +758,18 @@ VITA_GXM_RenderFillRects(SDL_Renderer *renderer, const SDL_RenderCommand *cmd)
 {
     VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata;
 
-    sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
-    sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
+    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);
+
+        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);
     sceGxmDraw(data->gxm_context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, data->linearIndices, 4 * cmd->data.draw.count);
@@ -821,40 +844,65 @@ VITA_GXM_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *
                 b = cmd->data.draw.b;
                 a = cmd->data.draw.a;
 
-                sceGxmSetVertexProgram(data->gxm_context, data->textureVertexProgram);
+                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)
                 {
-                    sceGxmSetFragmentProgram(data->gxm_context, data->textureFragmentProgram);
+                    if (data->drawstate.fragment_program != data->textureFragmentProgram) {
+                        data->drawstate.fragment_program = data->textureFragmentProgram;
+                        sceGxmSetFragmentProgram(data->gxm_context, data->textureFragmentProgram);
+                    }
                 }
                 else
                 {
-                    sceGxmSetFragmentProgram(data->gxm_context, data->textureTintFragmentProgram);
-                    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)
-                    );
+                    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);
+                    }
 
-                    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);
                 }
 
-                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;
 
-                VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *) cmd->data.draw.texture->driverdata;
+                    sceGxmSetFragmentTexture(data->gxm_context, 0, &vita_texture->tex->gxm_tex);
+                }
 
-                sceGxmSetFragmentTexture(data->gxm_context, 0, &vita_texture->tex->gxm_tex);
 
                 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);
@@ -865,7 +913,7 @@ VITA_GXM_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *
             case SDL_RENDERCMD_NO_OP:
                 break;
         }
-
+        data->drawstate.last_command = cmd->command;
         cmd = cmd->next;
     }
 
diff --git a/src/render/vitagxm/SDL_render_vita_gxm_tools.c b/src/render/vitagxm/SDL_render_vita_gxm_tools.c
index 26967aa69..757f1f495 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm_tools.c
+++ b/src/render/vitagxm/SDL_render_vita_gxm_tools.c
@@ -249,6 +249,8 @@ set_stencil_mask(VITA_GXM_RenderData *data, float x, float y, float w, float h)
     vertices[3].z = +0.5f;
     vertices[3].color = 0;
 
+    data->drawstate.fragment_program = data->colorFragmentProgram;
+    data->drawstate.vertex_program = data->colorVertexProgram;
     sceGxmSetVertexProgram(data->gxm_context, data->colorVertexProgram);
     sceGxmSetFragmentProgram(data->gxm_context, data->colorFragmentProgram);
 
diff --git a/src/render/vitagxm/SDL_render_vita_gxm_types.h b/src/render/vitagxm/SDL_render_vita_gxm_types.h
index cc4e5f22a..af4c3316d 100644
--- a/src/render/vitagxm/SDL_render_vita_gxm_types.h
+++ b/src/render/vitagxm/SDL_render_vita_gxm_types.h
@@ -104,6 +104,10 @@ typedef struct
     SDL_Texture *texture;
     SDL_Texture *target;
     Uint32 color;
+    Uint32 texture_color;
+    SceGxmFragmentProgram *fragment_program;
+    SceGxmVertexProgram *vertex_program;
+    int last_command;
 } gxm_drawstate_cache;
 
 typedef struct