[3/4] wined3d: Lookup GLSL float uniform locations only once per program

H. Verbeet hverbeet at gmail.com
Sat Aug 19 10:23:20 CDT 2006


Currently GLSL uniform locations are looked up every time a constant
is loaded. Since this happens quite a lot, the lookup is rather
expensive. This patch should increase performance a bit for
applications that use shaders, when GLSL is enabled.

Changelog:
  - Lookup GLSL float uniform locations only once per program
-------------- next part --------------
 dlls/wined3d/device.c          |   29 ++++++++++++++++++++----
 dlls/wined3d/drawprim.c        |   11 +++++----
 dlls/wined3d/glsl_shader.c     |   49 +++++++++++++++++-----------------------
 dlls/wined3d/stateblock.c      |    6 ++---
 dlls/wined3d/wined3d_private.h |    4 ++-
 5 files changed, 57 insertions(+), 42 deletions(-)

diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 3abd963..5f39a42 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -7,6 +7,7 @@
  * Copyright 2004 Christian Costa
  * Copyright 2005 Oliver Stieber
  * Copyright 2006 Stefan D?singer for CodeWeavers
+ * Copyright 2006 Henri Verbeet
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -246,9 +247,9 @@ static void attach_glsl_shader(IWineD3DD
 
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
     GLhandleARB shaderObj = ((IWineD3DBaseShaderImpl*)shader)->baseShader.prgId;
-    if (This->stateBlock->shaderPrgId != 0 && shaderObj != 0) {
-        TRACE_(d3d_shader)("Attaching GLSL shader object %u to program %u\n", shaderObj, This->stateBlock->shaderPrgId);
-        GL_EXTCALL(glAttachObjectARB(This->stateBlock->shaderPrgId, shaderObj));
+    if (This->stateBlock->glsl_program && shaderObj != 0) {
+        TRACE_(d3d_shader)("Attaching GLSL shader object %u to program %u\n", shaderObj, This->stateBlock->glsl_program->programId);
+        GL_EXTCALL(glAttachObjectARB(This->stateBlock->glsl_program->programId, shaderObj));
         checkGLcall("glAttachObjectARB");
     }
 }
@@ -278,6 +279,8 @@ void set_glsl_shader_program(IWineD3DDev
     struct glsl_shader_prog_link *newLink  = NULL;
     struct list *ptr                       = NULL;
     GLhandleARB programId                  = 0;
+    int i;
+    char glsl_name[8];
     
     ptr = list_head( &This->glsl_shader_progs );
     while (ptr) {
@@ -287,7 +290,7 @@ void set_glsl_shader_program(IWineD3DDev
             /* Existing Program found, use it */
             TRACE_(d3d_shader)("Found existing program (%u) for this vertex/pixel shader combination\n", 
                    curLink->programId);
-            This->stateBlock->shaderPrgId = curLink->programId;
+            This->stateBlock->glsl_program = curLink;
             return;
         }
         /* This isn't the entry we need - try the next one */
@@ -297,11 +300,11 @@ void set_glsl_shader_program(IWineD3DDev
     /* If we get to this point, then no matching program exists, so we create one */
     programId = GL_EXTCALL(glCreateProgramObjectARB());
     TRACE_(d3d_shader)("Created new GLSL shader program %u\n", programId);
-    This->stateBlock->shaderPrgId = programId;
 
     /* Allocate a new link for the list of programs */
     newLink = HeapAlloc(GetProcessHeap(), 0, sizeof(struct glsl_shader_prog_link));
     newLink->programId    = programId;
+    This->stateBlock->glsl_program = newLink;
    
     /* Attach GLSL vshader */ 
     if (NULL != vshader && wined3d_settings.vs_selected_mode == SHADER_GLSL) {
@@ -341,6 +344,18 @@ void set_glsl_shader_program(IWineD3DDev
     GL_EXTCALL(glLinkProgramARB(programId));
     print_glsl_info_log(&GLINFO_LOCATION, programId);
     list_add_head( &This->glsl_shader_progs, &newLink->entry);
+
+    newLink->vuniformF_locations = HeapAlloc(GetProcessHeap(), 0, sizeof(GLhandleARB) * GL_LIMITS(vshader_constantsF));
+    for (i = 0; i < GL_LIMITS(vshader_constantsF); ++i) {
+        snprintf(glsl_name, sizeof(glsl_name), "VC[%i]", i);
+        newLink->vuniformF_locations[i] = GL_EXTCALL(glGetUniformLocationARB(programId, glsl_name));
+    }
+    newLink->puniformF_locations = HeapAlloc(GetProcessHeap(), 0, sizeof(GLhandleARB) * GL_LIMITS(pshader_constantsF));
+    for (i = 0; i < GL_LIMITS(pshader_constantsF); ++i) {
+        snprintf(glsl_name, sizeof(glsl_name), "PC[%i]", i);
+        newLink->puniformF_locations[i] = GL_EXTCALL(glGetUniformLocationARB(programId, glsl_name));
+    }
+
     return;
 }
 
@@ -401,6 +416,10 @@ static void delete_glsl_shader_list(IWin
         
         delete_glsl_shader_program(iface, curLink->programId);
 
+        /* Free the uniform locations */
+        HeapFree(GetProcessHeap(), 0, curLink->vuniformF_locations);
+        HeapFree(GetProcessHeap(), 0, curLink->puniformF_locations);
+
         /* Free the memory for this list item */    
         HeapFree(GetProcessHeap(), 0, curLink);
     }
diff --git a/dlls/wined3d/drawprim.c b/dlls/wined3d/drawprim.c
index 360efdd..5eb71f1 100644
--- a/dlls/wined3d/drawprim.c
+++ b/dlls/wined3d/drawprim.c
@@ -1791,20 +1791,21 @@ #undef BUFFER_OR_DATA
     }
 
     /* If GLSL is used for either pixel or vertex shaders, make a GLSL program 
-     * Otherwise set 0, which restores fixed function */
+     * Otherwise set NULL, to restore fixed function */
     if ((wined3d_settings.vs_selected_mode == SHADER_GLSL && useVertexShaderFunction) ||
         (wined3d_settings.ps_selected_mode == SHADER_GLSL && usePixelShaderFunction)) 
         set_glsl_shader_program(iface);
     else
-        This->stateBlock->shaderPrgId = 0;
+        This->stateBlock->glsl_program = NULL;
 
     /* If GLSL is used now, or might have been used before, (re)set the program */
     if (wined3d_settings.vs_selected_mode == SHADER_GLSL || 
         wined3d_settings.ps_selected_mode == SHADER_GLSL) {
 
-        if (This->stateBlock->shaderPrgId) 
-            TRACE_(d3d_shader)("Using GLSL program %u\n", This->stateBlock->shaderPrgId);
-        GL_EXTCALL(glUseProgramObjectARB(This->stateBlock->shaderPrgId));
+        GLhandleARB progId = This->stateBlock->glsl_program ? This->stateBlock->glsl_program->programId : 0;
+        if (progId)
+            TRACE_(d3d_shader)("Using GLSL program %u\n", progId);
+        GL_EXTCALL(glUseProgramObjectARB(progId));
         checkGLcall("glUseProgramObjectARB");
     }
         
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index d222610..3c1d9ad 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -2,6 +2,7 @@
  * GLSL pixel and vertex shader implementation
  *
  * Copyright 2006 Jason Green 
+ * Copyright 2006 Henri Verbeet
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -55,7 +56,7 @@ void shader_glsl_load_psamplers(
     IWineD3DStateBlock* iface) {
 
     IWineD3DStateBlockImpl* stateBlock = (IWineD3DStateBlockImpl*) iface;
-    GLhandleARB programId = stateBlock->shaderPrgId;
+    GLhandleARB programId = stateBlock->glsl_program->programId;
     GLhandleARB name_loc;
     int i;
     char sampler_name[20];
@@ -77,19 +78,11 @@ void shader_glsl_load_psamplers(
  * Loads floating point constants (aka uniforms) into the currently set GLSL program.
  * When @constants_set == NULL, it will load all the constants.
  */
-void shader_glsl_load_constantsF(
-    IWineD3DBaseShaderImpl* This,
-    WineD3D_GL_Info *gl_info,
-    GLhandleARB programId,
-    unsigned max_constants,
-    float* constants,
-    BOOL* constants_set) {
-        
+void shader_glsl_load_constantsF(IWineD3DBaseShaderImpl* This, WineD3D_GL_Info *gl_info,
+        unsigned int max_constants, float* constants, GLhandleARB *constant_locations,
+        BOOL* constants_set) {
     GLhandleARB tmp_loc;
     int i;
-    char tmp_name[8];
-    char is_pshader = shader_is_pshader_version(This->baseShader.hex_version);
-    const char* prefix = is_pshader? "PC":"VC";
     struct list* ptr;
 
     for (i=0; i<max_constants; ++i) {
@@ -99,10 +92,7 @@ void shader_glsl_load_constantsF(
                   constants[i * 4 + 0], constants[i * 4 + 1],
                   constants[i * 4 + 2], constants[i * 4 + 3]);
 
-            /* TODO: Benchmark and see if it would be beneficial to store the 
-             * locations of the constants to avoid looking up each time */
-            snprintf(tmp_name, sizeof(tmp_name), "%s[%i]", prefix, i);
-            tmp_loc = GL_EXTCALL(glGetUniformLocationARB(programId, tmp_name));
+            tmp_loc = constant_locations[i];
             if (tmp_loc != -1) {
                 /* We found this uniform name in the program - go ahead and send the data */
                 GL_EXTCALL(glUniform4fvARB(tmp_loc, 1, &constants[i * 4]));
@@ -121,8 +111,7 @@ void shader_glsl_load_constantsF(
         TRACE("Loading local constants %i: %f, %f, %f, %f\n", idx,
             values[0], values[1], values[2], values[3]);
 
-        snprintf(tmp_name, sizeof(tmp_name), "%s[%i]", prefix, idx);
-        tmp_loc = GL_EXTCALL(glGetUniformLocationARB(programId, tmp_name));
+        tmp_loc = constant_locations[idx];
         if (tmp_loc != -1) {
             /* We found this uniform name in the program - go ahead and send the data */
             GL_EXTCALL(glUniform4fvARB(tmp_loc, 1, values));
@@ -258,12 +247,14 @@ void shader_glsl_load_constants(
     
     IWineD3DStateBlockImpl* stateBlock = (IWineD3DStateBlockImpl*) iface;
     WineD3D_GL_Info *gl_info = &((IWineD3DImpl*)stateBlock->wineD3DDevice->wineD3D)->gl_info;
-    GLhandleARB programId = stateBlock->shaderPrgId;
+    GLhandleARB *constant_locations;
+    GLhandleARB programId;
     
-    if (programId == 0) {
+    if (!stateBlock->glsl_program) {
         /* No GLSL program set - nothing to do. */
         return;
     }
+    programId = stateBlock->glsl_program->programId;
 
     if (useVertexShader) {
         IWineD3DBaseShaderImpl* vshader = (IWineD3DBaseShaderImpl*) stateBlock->vertexShader;
@@ -272,16 +263,17 @@ void shader_glsl_load_constants(
         IWineD3DVertexDeclarationImpl* vertexDeclaration =
             (IWineD3DVertexDeclarationImpl*) vshader_impl->vertexDeclaration;
 
+        constant_locations = stateBlock->glsl_program->vuniformF_locations;
+
         if (NULL != vertexDeclaration && NULL != vertexDeclaration->constants) {
             /* Load DirectX 8 float constants/uniforms for vertex shader */
-            shader_glsl_load_constantsF(vshader, gl_info, programId, GL_LIMITS(vshader_constantsF),
-                                        vertexDeclaration->constants, NULL);
+            shader_glsl_load_constantsF(vshader, gl_info, GL_LIMITS(vshader_constantsF),
+                    vertexDeclaration->constants, constant_locations, NULL);
         }
 
         /* Load DirectX 9 float constants/uniforms for vertex shader */
-        shader_glsl_load_constantsF(vshader, gl_info, programId, GL_LIMITS(vshader_constantsF),
-                                    stateBlock->vertexShaderConstantF, 
-                                    stateBlock->set.vertexShaderConstantsF);
+        shader_glsl_load_constantsF(vshader, gl_info, GL_LIMITS(vshader_constantsF),
+                stateBlock->vertexShaderConstantF, constant_locations, stateBlock->set.vertexShaderConstantsF);
 
         /* Load DirectX 9 integer constants/uniforms for vertex shader */
         shader_glsl_load_constantsI(vshader, gl_info, programId, MAX_CONST_I,
@@ -298,13 +290,14 @@ void shader_glsl_load_constants(
 
         IWineD3DBaseShaderImpl* pshader = (IWineD3DBaseShaderImpl*) stateBlock->pixelShader;
 
+        constant_locations = stateBlock->glsl_program->puniformF_locations;
+
         /* Load pixel shader samplers */
         shader_glsl_load_psamplers(gl_info, iface);
 
         /* Load DirectX 9 float constants/uniforms for pixel shader */
-        shader_glsl_load_constantsF(pshader, gl_info, programId, GL_LIMITS(pshader_constantsF),
-                                    stateBlock->pixelShaderConstantF,
-                                    stateBlock->set.pixelShaderConstantsF);
+        shader_glsl_load_constantsF(pshader, gl_info, GL_LIMITS(pshader_constantsF),
+                stateBlock->pixelShaderConstantF, constant_locations, stateBlock->set.pixelShaderConstantsF);
 
         /* Load DirectX 9 integer constants/uniforms for pixel shader */
         shader_glsl_load_constantsI(pshader, gl_info, programId, MAX_CONST_I,
diff --git a/dlls/wined3d/stateblock.c b/dlls/wined3d/stateblock.c
index ea77ecd..6dde429 100644
--- a/dlls/wined3d/stateblock.c
+++ b/dlls/wined3d/stateblock.c
@@ -166,7 +166,7 @@ void stateblock_copy(
     Dest->pixelShader = This->pixelShader;
     Dest->vertex_blend = This->vertex_blend;
     Dest->tween_factor = This->tween_factor;
-    Dest->shaderPrgId = This->shaderPrgId;
+    Dest->glsl_program = This->glsl_program;
 
     /* Fixed size arrays */
     memcpy(Dest->vertexShaderConstantB, This->vertexShaderConstantB, sizeof(BOOL) * MAX_CONST_B);
@@ -1067,9 +1067,9 @@ #endif
     }
     This->wineD3DDevice->currentPalette = 0;
 
-    /* Set default GLSL program ID to 0.  We won't actually create one
+    /* Set default GLSL program to NULL.  We won't actually create one
      * until the app sets a vertex or pixel shader */
-    This->shaderPrgId = 0;
+    This->glsl_program = NULL;
 
     TRACE("-----------------------> Device defaults now set up...\n");
     return WINED3D_OK;
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 0010062..bfa7510 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -1120,7 +1120,7 @@ struct IWineD3DStateBlockImpl
     DWORD                     samplerState[MAX_SAMPLERS][WINED3D_HIGHEST_SAMPLER_STATE + 1];
 
     /* Current GLSL Shader Program */
-    GLhandleARB               shaderPrgId;
+    struct glsl_shader_prog_link *glsl_program;
 };
 
 extern void stateblock_savedstates_set(
@@ -1304,6 +1304,8 @@ typedef void (*SHADER_HANDLER) (struct S
 struct glsl_shader_prog_link {
     struct list             entry;
     GLhandleARB             programId;
+    GLhandleARB             *vuniformF_locations;
+    GLhandleARB             *puniformF_locations;
     IWineD3DVertexShader*   vertexShader;
     IWineD3DPixelShader*    pixelShader;
 };


More information about the wine-patches mailing list