[4/6] wined3d: Store GLSL programs in a hash table rather than a
linked list
H. Verbeet
hverbeet at gmail.com
Tue Feb 27 13:51:53 CST 2007
Changelog:
- Store GLSL programs in a hash table rather than a linked list
-------------- next part --------------
---
dlls/wined3d/device.c | 82 +--------------------------
dlls/wined3d/directx.c | 21 +++++++
dlls/wined3d/glsl_shader.c | 123 ++++++++++++++++++++++------------------
dlls/wined3d/pixelshader.c | 11 +++-
dlls/wined3d/vertexshader.c | 11 +++-
dlls/wined3d/wined3d_private.h | 22 +++++--
6 files changed, 128 insertions(+), 142 deletions(-)
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index e614559..f73ceb7 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -32,7 +32,6 @@
#include "wined3d_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(d3d);
-WINE_DECLARE_DEBUG_CHANNEL(d3d_shader);
#define GLINFO_LOCATION ((IWineD3DImpl *)(This->wineD3D))->gl_info
/* Define the default light parameters as specified by MSDN */
@@ -99,6 +98,7 @@ static void set_depth_stencil_fbo(IWineD3DDevice *iface, IWineD3DSurface *depth_
object->parent = parent; \
object->ref = 1; \
object->baseShader.device = (IWineD3DDevice*) This; \
+ list_init(&object->baseShader.linked_programs); \
*pp##type = (IWineD3D##type *) object; \
}
@@ -149,76 +149,6 @@ static void set_depth_stencil_fbo(IWineD3DDevice *iface, IWineD3DSurface *depth_
const float identity[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; /* When needed for comparisons */
/**********************************************************
- * GLSL helper functions follow
- **********************************************************/
-
-/** Detach the GLSL pixel or vertex shader object from the shader program */
-static void detach_glsl_shader(IWineD3DDevice *iface, GLhandleARB shaderObj, GLhandleARB programId) {
-
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
-
- if (shaderObj != 0 && programId != 0) {
- TRACE_(d3d_shader)("Detaching GLSL shader object %u from program %u\n", shaderObj, programId);
- GL_EXTCALL(glDetachObjectARB(programId, shaderObj));
- checkGLcall("glDetachObjectARB");
- }
-}
-
-/** Delete a GLSL shader program */
-static void delete_glsl_shader_program(IWineD3DDevice *iface, GLhandleARB obj) {
-
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
-
- if (obj != 0) {
- TRACE_(d3d_shader)("Deleting GLSL shader program %u\n", obj);
- GL_EXTCALL(glDeleteObjectARB(obj));
- checkGLcall("glDeleteObjectARB");
- }
-}
-
-/** Delete the list of linked programs this shader is associated with.
- * Also at this point, check to see if there are any objects left attached
- * to each GLSL program. If not, delete the GLSL program object.
- * This will be run when a device is released. */
-static void delete_glsl_shader_list(IWineD3DDevice* iface) {
-
- struct list *ptr = NULL;
- struct glsl_shader_prog_link *curLink = NULL;
- IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
-
- int numAttached = 0;
- int i;
- GLhandleARB objList[2]; /* There should never be more than 2 objects attached
- (one pixel shader and one vertex shader at most) */
-
- ptr = list_head( &This->glsl_shader_progs );
- while (ptr) {
- /* First, get the current item,
- * save the link to the next pointer,
- * detach and delete shader objects,
- * then de-allocate the list item's memory */
- curLink = LIST_ENTRY( ptr, struct glsl_shader_prog_link, entry );
- ptr = list_next( &This->glsl_shader_progs, ptr );
-
- /* See if this object is still attached to the program - it may have been detached already */
- GL_EXTCALL(glGetAttachedObjectsARB(curLink->programId, 2, &numAttached, objList));
- TRACE_(d3d_shader)("%i GLSL objects are currently attached to program %u\n", numAttached, curLink->programId);
- for (i = 0; i < numAttached; i++) {
- detach_glsl_shader(iface, objList[i], curLink->programId);
- }
-
- 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);
- }
-}
-
-/**********************************************************
* IUnknown parts follows
**********************************************************/
@@ -261,15 +191,12 @@ static ULONG WINAPI IWineD3DDeviceImpl_Release(IWineD3DDevice *iface) {
HeapFree(GetProcessHeap(), 0, This->draw_buffers);
+ if (This->glsl_program_lookup) hash_table_destroy(This->glsl_program_lookup);
+
/* TODO: Clean up all the surfaces and textures! */
/* NOTE: You must release the parent if the object was created via a callback
** ***************************/
- /* Delete any GLSL shader programs that may exist */
- if (This->vs_selected_mode == SHADER_GLSL ||
- This->ps_selected_mode == SHADER_GLSL)
- delete_glsl_shader_list(iface);
-
/* Release the update stateblock */
if(IWineD3DStateBlock_Release((IWineD3DStateBlock *)This->updateStateBlock) > 0){
if(This->updateStateBlock != This->stateBlock)
@@ -1764,9 +1691,6 @@ static HRESULT WINAPI IWineD3DDeviceImpl_Init3D(IWineD3DDevice *iface, WINED3DPR
IWineD3DImpl_CheckGraphicsMemory();
#endif
- /* Initialize our list of GLSL programs */
- list_init(&This->glsl_shader_progs);
-
{ /* Set a default viewport */
WINED3DVIEWPORT vp;
vp.X = 0;
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 095b0e9..577e0db 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -2386,6 +2386,26 @@ static HRESULT WINAPI IWineD3DImpl_GetDeviceCaps(IWineD3D *iface, UINT Adapter,
return WINED3D_OK;
}
+static unsigned int glsl_program_key_hash(void *key) {
+ glsl_program_key_t *k = (glsl_program_key_t *)key;
+
+ unsigned int hash = k->vshader | k->pshader << 16;
+ hash += ~(hash << 15);
+ hash ^= (hash >> 10);
+ hash += (hash << 3);
+ hash ^= (hash >> 6);
+ hash += ~(hash << 11);
+ hash ^= (hash >> 16);
+
+ return hash;
+}
+
+static BOOL glsl_program_key_compare(void *keya, void *keyb) {
+ glsl_program_key_t *ka = (glsl_program_key_t *)keya;
+ glsl_program_key_t *kb = (glsl_program_key_t *)keyb;
+
+ return ka->vshader == kb->vshader && ka->pshader == kb->pshader;
+}
/* Note due to structure differences between dx8 and dx9 D3DPRESENT_PARAMETERS,
and fields being inserted in the middle, a new structure is used in place */
@@ -2456,6 +2476,7 @@ static HRESULT WINAPI IWineD3DImpl_CreateDevice(IWineD3D *iface, UINT Adapter,
select_shader_mode(&This->gl_info, DeviceType, &object->ps_selected_mode, &object->vs_selected_mode);
if (object->ps_selected_mode == SHADER_GLSL || object->vs_selected_mode == SHADER_GLSL) {
object->shader_backend = &glsl_shader_backend;
+ object->glsl_program_lookup = hash_table_create(&glsl_program_key_hash, &glsl_program_key_compare);
} else if (object->ps_selected_mode == SHADER_ARB || object->vs_selected_mode == SHADER_ARB) {
object->shader_backend = &arb_program_shader_backend;
} else {
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index f270af7..ae2bf08 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -1994,16 +1994,42 @@ void vshader_glsl_output_unpack(
}
}
-/** Attach a GLSL pixel or vertex shader object to the shader program */
-static void attach_glsl_shader(IWineD3DDevice *iface, IWineD3DBaseShader* shader) {
+static void add_glsl_program_entry(IWineD3DDeviceImpl *device, struct glsl_shader_prog_link *entry) {
+ glsl_program_key_t *key;
+
+ key = HeapAlloc(GetProcessHeap(), 0, sizeof(glsl_program_key_t));
+ key->vshader = entry->vshader;
+ key->pshader = entry->pshader;
+
+ hash_table_put(device->glsl_program_lookup, key, entry);
+}
+
+static struct glsl_shader_prog_link *get_glsl_program_entry(IWineD3DDeviceImpl *device,
+ GLhandleARB vshader, GLhandleARB pshader) {
+ glsl_program_key_t key;
+
+ key.vshader = vshader;
+ key.pshader = pshader;
+
+ return (struct glsl_shader_prog_link *)hash_table_get(device->glsl_program_lookup, &key);
+}
+
+void delete_glsl_program_entry(IWineD3DDevice *iface, struct glsl_shader_prog_link *entry) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
WineD3D_GL_Info *gl_info = &((IWineD3DImpl *)(This->wineD3D))->gl_info;
- GLhandleARB shaderObj = ((IWineD3DBaseShaderImpl*)shader)->baseShader.prgId;
- if (This->stateBlock->glsl_program && shaderObj != 0) {
- TRACE("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");
- }
+ glsl_program_key_t *key;
+
+ key = HeapAlloc(GetProcessHeap(), 0, sizeof(glsl_program_key_t));
+ key->vshader = entry->vshader;
+ key->pshader = entry->pshader;
+ hash_table_remove(This->glsl_program_lookup, key);
+
+ GL_EXTCALL(glDeleteObjectARB(entry->programId));
+ if (entry->vshader) list_remove(&entry->vshader_entry);
+ if (entry->pshader) list_remove(&entry->pshader_entry);
+ HeapFree(GetProcessHeap(), 0, entry->vuniformF_locations);
+ HeapFree(GetProcessHeap(), 0, entry->puniformF_locations);
+ HeapFree(GetProcessHeap(), 0, entry);
}
/** Sets the GLSL program ID for the given pixel and vertex shader combination.
@@ -2011,61 +2037,50 @@ static void attach_glsl_shader(IWineD3DDevice *iface, IWineD3DBaseShader* shader
* inside of the DrawPrimitive() part of the render loop).
*
* If a program for the given combination does not exist, create one, and store
- * the program in the list. If it creates a program, it will link the given
- * objects, too.
- *
- * We keep the shader programs around on a list because linking
- * shader objects together is an expensive operation. It's much
- * faster to loop through a list of pre-compiled & linked programs
- * each time that the application sets a new pixel or vertex shader
- * than it is to re-link them together at that time.
- *
- * The list will be deleted in IWineD3DDevice::Release().
+ * the program in the hash table. If it creates a program, it will link the
+ * given objects, too.
*/
static void set_glsl_shader_program(IWineD3DDevice *iface) {
IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
WineD3D_GL_Info *gl_info = &((IWineD3DImpl *)(This->wineD3D))->gl_info;
IWineD3DPixelShader *pshader = This->stateBlock->pixelShader;
IWineD3DVertexShader *vshader = This->stateBlock->vertexShader;
- struct glsl_shader_prog_link *curLink = NULL;
- struct glsl_shader_prog_link *newLink = NULL;
- struct list *ptr = NULL;
+ struct glsl_shader_prog_link *entry = NULL;
GLhandleARB programId = 0;
int i;
char glsl_name[8];
- ptr = list_head( &This->glsl_shader_progs );
- while (ptr) {
- /* At least one program exists - see if it matches our ps/vs combination */
- curLink = LIST_ENTRY( ptr, struct glsl_shader_prog_link, entry );
- if (vshader == curLink->vertexShader && pshader == curLink->pixelShader) {
- /* Existing Program found, use it */
- TRACE("Found existing program (%u) for this vertex/pixel shader combination\n",
- curLink->programId);
- This->stateBlock->glsl_program = curLink;
- return;
- }
- /* This isn't the entry we need - try the next one */
- ptr = list_next( &This->glsl_shader_progs, ptr );
+ GLhandleARB vshader_id = (vshader && This->vs_selected_mode == SHADER_GLSL) ? ((IWineD3DBaseShaderImpl*)vshader)->baseShader.prgId : 0;
+ GLhandleARB pshader_id = (pshader && This->ps_selected_mode == SHADER_GLSL) ? ((IWineD3DBaseShaderImpl*)pshader)->baseShader.prgId : 0;
+ entry = get_glsl_program_entry(This, vshader_id, pshader_id);
+ if (entry) {
+ This->stateBlock->glsl_program = entry;
+ return;
}
/* If we get to this point, then no matching program exists, so we create one */
programId = GL_EXTCALL(glCreateProgramObjectARB());
TRACE("Created new GLSL shader program %u\n", 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;
+ /* Create the entry */
+ entry = HeapAlloc(GetProcessHeap(), 0, sizeof(struct glsl_shader_prog_link));
+ entry->programId = programId;
+ entry->vshader = vshader_id;
+ entry->pshader = pshader_id;
+ /* Add the hash table entry */
+ add_glsl_program_entry(This, entry);
+
+ /* Set the current program */
+ This->stateBlock->glsl_program = entry;
/* Attach GLSL vshader */
- if (NULL != vshader && This->vs_selected_mode == SHADER_GLSL) {
- int i;
+ if (vshader_id) {
int max_attribs = 16; /* TODO: Will this always be the case? It is at the moment... */
char tmp_name[10];
- TRACE("Attaching vertex shader to GLSL program\n");
- attach_glsl_shader(iface, (IWineD3DBaseShader*)vshader);
+ TRACE("Attaching GLSL shader object %u to program %u\n", vshader_id, programId);
+ GL_EXTCALL(glAttachObjectARB(programId, vshader_id));
+ checkGLcall("glAttachObjectARB");
/* Bind vertex attributes to a corresponding index number to match
* the same index numbers as ARB_vertex_programs (makes loading
@@ -2081,34 +2096,34 @@ static void set_glsl_shader_program(IWineD3DDevice *iface) {
GL_EXTCALL(glBindAttribLocationARB(programId, i, tmp_name));
}
checkGLcall("glBindAttribLocationARB");
- newLink->vertexShader = vshader;
+
+ list_add_head(&((IWineD3DBaseShaderImpl *)vshader)->baseShader.linked_programs, &entry->vshader_entry);
}
/* Attach GLSL pshader */
- if (NULL != pshader && This->ps_selected_mode == SHADER_GLSL) {
- TRACE("Attaching pixel shader to GLSL program\n");
- attach_glsl_shader(iface, (IWineD3DBaseShader*)pshader);
- newLink->pixelShader = pshader;
+ if (pshader_id) {
+ TRACE("Attaching GLSL shader object %u to program %u\n", pshader_id, programId);
+ GL_EXTCALL(glAttachObjectARB(programId, pshader_id));
+ checkGLcall("glAttachObjectARB");
+
+ list_add_head(&((IWineD3DBaseShaderImpl *)pshader)->baseShader.linked_programs, &entry->pshader_entry);
}
/* Link the program */
TRACE("Linking GLSL shader program %u\n", programId);
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));
+ entry->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));
+ entry->vuniformF_locations[i] = GL_EXTCALL(glGetUniformLocationARB(programId, glsl_name));
}
- newLink->puniformF_locations = HeapAlloc(GetProcessHeap(), 0, sizeof(GLhandleARB) * GL_LIMITS(pshader_constantsF));
+ entry->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));
+ entry->puniformF_locations[i] = GL_EXTCALL(glGetUniformLocationARB(programId, glsl_name));
}
-
- return;
}
static GLhandleARB create_glsl_blt_shader(WineD3D_GL_Info *gl_info) {
diff --git a/dlls/wined3d/pixelshader.c b/dlls/wined3d/pixelshader.c
index e83cab1..e362f65 100644
--- a/dlls/wined3d/pixelshader.c
+++ b/dlls/wined3d/pixelshader.c
@@ -74,7 +74,16 @@ static ULONG WINAPI IWineD3DPixelShaderImpl_Release(IWineD3DPixelShader *iface)
ref = InterlockedDecrement(&This->ref);
if (ref == 0) {
if (This->baseShader.shader_mode == SHADER_GLSL && This->baseShader.prgId != 0) {
- /* If this shader is still attached to a program, GL will perform a lazy delete */
+ struct list *linked_programs = &This->baseShader.linked_programs;
+
+ TRACE("Deleting linked programs\n");
+ if (linked_programs->next) {
+ struct glsl_shader_prog_link *entry, *entry2;
+ LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, struct glsl_shader_prog_link, pshader_entry) {
+ delete_glsl_program_entry(This->baseShader.device, entry);
+ }
+ }
+
TRACE("Deleting shader object %u\n", This->baseShader.prgId);
GL_EXTCALL(glDeleteObjectARB(This->baseShader.prgId));
checkGLcall("glDeleteObjectARB");
diff --git a/dlls/wined3d/vertexshader.c b/dlls/wined3d/vertexshader.c
index f027032..5e79451 100644
--- a/dlls/wined3d/vertexshader.c
+++ b/dlls/wined3d/vertexshader.c
@@ -1113,7 +1113,16 @@ static ULONG WINAPI IWineD3DVertexShaderImpl_Release(IWineD3DVertexShader *iface
ref = InterlockedDecrement(&This->ref);
if (ref == 0) {
if (This->baseShader.shader_mode == SHADER_GLSL && This->baseShader.prgId != 0) {
- /* If this shader is still attached to a program, GL will perform a lazy delete */
+ struct list *linked_programs = &This->baseShader.linked_programs;
+
+ TRACE("Deleting linked programs\n");
+ if (linked_programs->next) {
+ struct glsl_shader_prog_link *entry, *entry2;
+ LIST_FOR_EACH_ENTRY_SAFE(entry, entry2, linked_programs, struct glsl_shader_prog_link, vshader_entry) {
+ delete_glsl_program_entry(This->baseShader.device, entry);
+ }
+ }
+
TRACE("Deleting shader object %u\n", This->baseShader.prgId);
GL_EXTCALL(glDeleteObjectARB(This->baseShader.prgId));
checkGLcall("glDeleteObjectARB");
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index c228574..add82a8 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -628,6 +628,7 @@ struct IWineD3DDeviceImpl
int vs_selected_mode;
int ps_selected_mode;
const shader_backend_t *shader_backend;
+ hash_table_t *glsl_program_lookup;
/* To store */
BOOL view_ident; /* true iff view matrix is identity */
@@ -701,10 +702,6 @@ struct IWineD3DDeviceImpl
WINED3DFORMAT ddraw_format;
BOOL ddraw_fullscreen;
- /* List of GLSL shader programs and their associated vertex & pixel shaders */
- struct list glsl_shader_progs;
-
-
/* Final position fixup constant */
float posFixup[4];
@@ -1463,14 +1460,20 @@ typedef void (*SHADER_HANDLER) (struct SHADER_OPCODE_ARG*);
* vertex shaders. A list of this type is maintained on the DeviceImpl, and is only
* used if the user is using GLSL shaders. */
struct glsl_shader_prog_link {
- struct list entry;
+ struct list vshader_entry;
+ struct list pshader_entry;
GLhandleARB programId;
GLhandleARB *vuniformF_locations;
GLhandleARB *puniformF_locations;
- IWineD3DVertexShader* vertexShader;
- IWineD3DPixelShader* pixelShader;
+ GLhandleARB vshader;
+ GLhandleARB pshader;
};
+typedef struct {
+ GLhandleARB vshader;
+ GLhandleARB pshader;
+} glsl_program_key_t;
+
/* TODO: Make this dynamic, based on shader limits ? */
#define MAX_REG_ADDR 1
#define MAX_REG_TEMP 32
@@ -1600,6 +1603,8 @@ extern const SHADER_OPCODE* shader_get_opcode(
extern void shader_delete_constant_list(
struct list* clist);
+void delete_glsl_program_entry(IWineD3DDevice *iface, struct glsl_shader_prog_link *entry);
+
/* Vertex shader utility functions */
extern BOOL vshader_get_input(
IWineD3DVertexShader* iface,
@@ -1727,6 +1732,9 @@ typedef struct IWineD3DBaseShaderClass
/* Type of shader backend */
int shader_mode;
+ /* Programs this shader is linked with */
+ struct list linked_programs;
+
/* Immediate constants (override global ones) */
struct list constantsB;
struct list constantsF;
More information about the wine-patches
mailing list