Commit 2c85e5e8 authored by H. Verbeet's avatar H. Verbeet Committed by Alexandre Julliard

wined3d: Store GLSL programs in a hash table rather than a linked list.

parent 2a82ed89
......@@ -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;
......
......@@ -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 {
......
......@@ -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) {
......
......@@ -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");
......
......@@ -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");
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment