Sampler loading support in OpenGL glue. trunk
authorRyan C. Gordon <icculus@icculus.org>
Tue, 06 May 2008 00:26:59 -0400
branchtrunk
changeset 284 ea52f9707795
parent 283 9d51531ffd49
child 285 5e7d6cf65f1e
Sampler loading support in OpenGL glue.
mojoshader.c
mojoshader.h
mojoshader_opengl.c
--- a/mojoshader.c	Mon May 05 22:17:17 2008 -0400
+++ b/mojoshader.c	Tue May 06 00:26:59 2008 -0400
@@ -3757,6 +3757,9 @@
 
     // !!! FIXME: fail if DCL opcode comes after real instructions.
 
+    // !!! FIXME: apparently vs_3_0 can use sampler registers now.
+    // !!! FIXME:  (but only s0 through s3, not all 16 of them.)
+
     if (shader_is_vertex(ctx))
     {
         const MOJOSHADER_usage usage = (const MOJOSHADER_usage) ctx->dwords[0];
@@ -4986,6 +4989,10 @@
 
 static void process_definitions(Context *ctx)
 {
+    // !!! FIXME: apparently, pre ps_3_0, sampler registers don't need to be
+    // !!! FIXME:  DCL'd before use (default to 2d?). We aren't checking
+    // !!! FIXME:  this at the moment, though.
+
     RegisterList *uitem = &ctx->uniforms;
     RegisterList *prev = &ctx->used_registers;
     RegisterList *item = prev->next;
--- a/mojoshader.h	Mon May 05 22:17:17 2008 -0400
+++ b/mojoshader.h	Tue May 06 00:26:59 2008 -0400
@@ -767,6 +767,37 @@
                                      const void *ptr);
 
 /*
+ * Set a sampler.
+ *
+ * There is a single array of samplers shared by all shaders.
+ *  This is the "s" register file in Direct3D (s0, s1, s2, etc...), also
+ *  known as the "sampler stages." MojoShader will take care of synchronizing
+ *  this internal array with the appropriate variables in the GL shaders.
+ *
+ * Unlike the float and int counterparts, samplers are single values, not
+ *  four-element vectors...so idx==1 is the second sampler in the internal
+ *  array, not the fifth.
+ *
+ * (sampler) is the index into the internal array.
+ * (unit) is an OpenGL TEXTURE UNIT (not a texture name!). This is the
+ *  unit's index, not the GLenum. So if you want GL_TEXTURE_0, use 0.
+ *
+ * Bind the texture unit, set state, call this to assign it to the internal
+ *  array, where it will propagate to the shader during
+ *  MOJOSHADER_glProgramReady().
+ *
+ * This call is NOT thread safe! As most OpenGL implementations are not thread
+ *  safe, you should probably only call this from the same thread that created
+ *  the GL context.
+ *
+ * This call requires a valid MOJOSHADER_glContext to have been made current,
+ *  or it will crash your program. See MOJOSHADER_glMakeContextCurrent().
+ *
+ * Samplers are not shared between contexts.
+ */
+void MOJOSHADER_glSetSampler(unsigned int sampler, unsigned int unit);
+
+/*
  * Inform MojoShader that it should commit any pending state to the GL. This
  *  must be called after you bind a program and update any inputs, right
  *  before you start drawing, so any outstanding changes made to the shared
--- a/mojoshader_opengl.c	Mon May 05 22:17:17 2008 -0400
+++ b/mojoshader_opengl.c	Tue May 06 00:26:59 2008 -0400
@@ -61,6 +61,13 @@
 
 typedef struct
 {
+    MOJOSHADER_shaderType shader_type;
+    const MOJOSHADER_sampler *sampler;
+    GLuint location;
+} SamplerMap;
+
+typedef struct
+{
     const MOJOSHADER_attribute *attribute;
     GLuint location;
 } AttributeMap;
@@ -74,6 +81,8 @@
     GLfloat *constants;  // !!! FIXME: misnamed.
     uint32 uniform_count;
     UniformMap *uniforms;
+    uint32 sampler_count;
+    SamplerMap *samplers;
     uint32 attribute_count;
     AttributeMap *attributes;
     uint32 refcount;
@@ -102,6 +111,7 @@
     GLfloat ps_reg_file_f[8192 * 4];
     GLint ps_reg_file_i[2047 * 4];
     GLint ps_reg_file_b[2047];
+    GLuint sampler_reg_file[16];
 
     // GL stuff...
     int opengl_major;
@@ -524,9 +534,10 @@
             ctx->glDeleteObject(program->handle);
             shader_unref(program->vertex);
             shader_unref(program->fragment);
-            Free(program->attributes);
+            Free(program->constants);
+            Free(program->samplers);
             Free(program->uniforms);
-            Free(program->constants);
+            Free(program->attributes);
             Free(program);
         } // else
     } // if
@@ -556,6 +567,29 @@
 } // lookup_uniforms
 
 
+static void lookup_samplers(MOJOSHADER_glProgram *program,
+                            MOJOSHADER_glShader *shader)
+{
+    int i;
+    const MOJOSHADER_parseData *pd = shader->parseData;
+    const MOJOSHADER_sampler *s = pd->samplers;
+    const MOJOSHADER_shaderType shader_type = pd->shader_type;
+
+    for (i = 0; i < pd->sampler_count; i++)
+    {
+        const GLint loc = ctx->glGetUniformLocation(program->handle, s[i].name);
+        if (loc != -1)  // maybe the Sampler was optimized out?
+        {
+            SamplerMap *map = &program->samplers[program->sampler_count];
+            map->shader_type = shader_type;
+            map->sampler = &s[i];
+            map->location = (GLuint) loc;
+            program->sampler_count++;
+        } // if
+    } // for
+} // lookup_samplers
+
+
 static void lookup_attributes(MOJOSHADER_glProgram *program)
 {
     int i;
@@ -605,6 +639,7 @@
         goto link_program_fail;
     memset(retval, '\0', sizeof (MOJOSHADER_glProgram));
 
+    numregs = 0;
     if (vshader != NULL) numregs += vshader->parseData->uniform_count;
     if (pshader != NULL) numregs += pshader->parseData->uniform_count;
     retval->uniforms = (UniformMap *) Malloc(sizeof (UniformMap) * numregs);
@@ -612,6 +647,14 @@
         goto link_program_fail;
     memset(retval->uniforms, '\0', sizeof (UniformMap) * numregs);
 
+    numregs = 0;
+    if (vshader != NULL) numregs += vshader->parseData->sampler_count;
+    if (pshader != NULL) numregs += pshader->parseData->sampler_count;
+    retval->samplers = (SamplerMap *) Malloc(sizeof (SamplerMap) * numregs);
+    if (retval->samplers == NULL)
+        goto link_program_fail;
+    memset(retval->samplers, '\0', sizeof (SamplerMap) * numregs);
+
     retval->handle = program;
     retval->vertex = vshader;
     retval->fragment = pshader;
@@ -630,6 +673,7 @@
 
         lookup_attributes(retval);
         lookup_uniforms(retval, vshader);
+        lookup_samplers(retval, vshader);
         vshader->refcount++;
     } // if
 
@@ -639,6 +683,7 @@
             const_count = pshader->parseData->constant_count;
 
         lookup_uniforms(retval, pshader);
+        lookup_samplers(retval, vshader);
         pshader->refcount++;
     } // if
 
@@ -655,9 +700,10 @@
 link_program_fail:
     if (retval != NULL)
     {
+        Free(retval->constants);
+        Free(retval->samplers);
         Free(retval->uniforms);
         Free(retval->attributes);
-        Free(retval->constants);
         Free(retval);
     } // if
 
@@ -785,6 +831,14 @@
 } // MOJOSHADER_glSetPixelShaderUniformB
 
 
+void MOJOSHADER_glSetSampler(unsigned int idx, unsigned int unit)
+{
+    const uint maxregs = STATICARRAYLEN(ctx->sampler_reg_file);
+    if (idx < maxregs)
+        ctx->sampler_reg_file[idx] = (GLuint) unit;
+} // MOJOSHADER_glSetSampler
+
+
 static inline GLenum opengl_attr_type(const MOJOSHADER_attributeType type)
 {
     switch (type)
@@ -809,6 +863,7 @@
 } // opengl_attr_type
 
 
+// !!! FIXME: shouldn't (index) be unsigned?
 void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage,
                                      int index, unsigned int size,
                                      MOJOSHADER_attributeType type,
@@ -857,14 +912,15 @@
 void MOJOSHADER_glProgramReady(void)
 {
     int i;
+    int count;
 
     if (ctx->bound_program == NULL)
         return;  // nothing to do.
 
-    // !!! FIXME: don't push Uniforms if we know they haven't changed.
+    // !!! FIXME: don't push Uniforms/Samplers if they haven't changed.
 
     // push Uniforms to the program from our register files...
-    const int count = ctx->bound_program->uniform_count;
+    count = ctx->bound_program->uniform_count;
     for (i = 0; i < count; i++)
     {
         const UniformMap *map = &ctx->bound_program->uniforms[i];
@@ -980,6 +1036,15 @@
                 ctx->glUniform1i(location, ctx->ps_reg_file_b[index]);
         } // else if
     } // for
+
+    // push Samplers to the program from our register files...
+    count = ctx->bound_program->sampler_count;
+    for (i = 0; i < count; i++)
+    {
+        const SamplerMap *map = &ctx->bound_program->samplers[i];
+        const MOJOSHADER_sampler *s = map->sampler;
+        ctx->glUniform1i(map->location, ctx->sampler_reg_file[s->index]);
+    } // for
 } // MOJOSHADER_glProgramReady