PDA

View Full Version : AMD bug with samplers



l_belev
01-21-2012, 07:32 AM
Its a bit involved to explain but bear with me.
The setup is as follows:
- single texture bound to 2 different texture units (say 0 and 1)
- 2 sampler objects, one for each tex unit and bound to them.
- the sampler objects have different sampling states, say 0 has mag filter LINEAR and 1 has mag filter NEAREST.
- a fragment shader uses both samplers to fetch the (same) texture.

Then the fetched data from the 2 samplers use the sampling states from only one of the sampler objects - both are either LINEAR or NEAREST. I'm not sure how the choice is made, maybe the sampler object which was set latest is used.

here is simple win32-based code to reproduce the bug
to display the result, the fragment shader outputs sample0 to the red channel and sample1 to the green channel - one of them should be filtered and the other not, but they are both the same.



#include <windows.h>
#include <GL/gl.h>
#include "glext.h"

#define OGLENTRY __stdcall

void (OGLENTRY *_glEnableVertexAttribArray) (GLuint index);
void (OGLENTRY *_glVertexAttribPointer) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
void (OGLENTRY *_glBufferData) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
void (OGLENTRY *_glBindBufferBase) (GLenum target, GLuint index, GLuint buffer);
void (OGLENTRY *_glGenBuffers) (GLsizei n, GLuint *buffers);
void (OGLENTRY *_glSamplerParameteri) (GLuint sampler, GLenum pname, GLint param);
void (OGLENTRY *_glBindSampler) (GLuint unit, GLuint sampler);
GLuint (OGLENTRY *_glCreateShader) (GLenum type);
void (OGLENTRY *_glShaderSource) (GLuint shader, GLsizei count, const GLchar* *string, const GLint *length);
void (OGLENTRY *_glCompileShader) (GLuint shader);
void (OGLENTRY *_glGetShaderiv) (GLuint shader, GLenum pname, GLint *params);
GLuint (OGLENTRY *_glCreateProgram) (void);
void (OGLENTRY *_glAttachShader) (GLuint program, GLuint shader);
void (OGLENTRY *_glLinkProgram) (GLuint program);
void (OGLENTRY *_glGetProgramiv) (GLuint program, GLenum pname, GLint *params);
void (OGLENTRY *_glUseProgram) (GLuint program);
GLint (OGLENTRY *_glGetUniformLocation) (GLuint program, const GLchar *name);
void (OGLENTRY *_glUniform1i) (GLint location, GLint v0);
void (OGLENTRY *_glActiveTexture) (GLenum texture);
void (OGLENTRY *_glGenSamplers) (GLsizei count, GLuint *samplers);
void (OGLENTRY *_glBindBuffer) (GLenum target, GLuint buffer);
void (OGLENTRY *_glGetShaderInfoLog) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
void (OGLENTRY *_glGetProgramInfoLog) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);


void init()
{
static const char vs_text[] =
"#version 330\n"
"layout(location=0) in vec2 a2v_pos;\n"
"layout(location=1) in vec2 a2v_tc;\n"
"out vec2 v2f_tc;\n"
"void main() {\n"
" v2f_tc = a2v_tc;\n"
" gl_Position = vec4(a2v_pos, 0, 1);\n"
"}";

static const char fs_text[] =
"#version 330\n"
"uniform sampler2D s0, s1;\n"
"in vec2 v2f_tc;\n"
"layout(location=0) out vec4 c0;\n"
"void main() {\n"
" vec4 f0 = texture(s0, v2f_tc);\n"
" vec4 f1 = texture(s1, v2f_tc);\n"
" c0 = vec4(f0.x, f1.x, 0, 0);\n"
"}";

static const short image[] = {
0, -1, 0, -1,
-1, 0, -1, 0,
0, -1, 0, -1,
-1, 0, -1, 0,
};

static const struct { float x, y, s, t; } verts[4] = {
-1, -1, 0, 0,
+1, -1, 1, 0,
-1, +1, 0, 1,
+1, +1, 1, 1,
};

GLenum status;
GLint len, i;
GLuint vs, fs, prog, tex, sam[2], vb;
const char *txt;


#define GET(f) *(void **)&amp;_ ## f = wglGetProcAddress( # f )
GET(glEnableVertexAttribArray);
GET(glVertexAttribPointer);
GET(glBufferData);
GET(glBindBufferBase);
GET(glGenBuffers);
GET(glSamplerParameteri);
GET(glBindSampler);
GET(glCreateShader);
GET(glShaderSource);
GET(glCompileShader);
GET(glGetShaderiv);
GET(glCreateProgram);
GET(glAttachShader);
GET(glLinkProgram);
GET(glGetProgramiv);
GET(glUseProgram);
GET(glGetUniformLocation);
GET(glUniform1i);
GET(glActiveTexture);
GET(glGenSamplers);
GET(glBindBuffer);
GET(glGetShaderInfoLog);
GET(glGetProgramInfoLog);


vs = _glCreateShader(GL_VERTEX_SHADER);
txt = vs_text;
len = sizeof(vs_text);
_glShaderSource(vs, 1, &amp;txt, &amp;len);
_glCompileShader(vs);
_glGetShaderiv(vs, GL_COMPILE_STATUS, &amp;status);
if (status == GL_FALSE) _asm int 3

fs = _glCreateShader(GL_FRAGMENT_SHADER);
txt = fs_text;
len = sizeof(fs_text);
_glShaderSource(fs, 1, &amp;txt, &amp;len);
_glCompileShader(fs);
_glGetShaderiv(fs, GL_COMPILE_STATUS, &amp;status);
if (status == GL_FALSE) {
char log[65536];
GLint log_size;
_glGetShaderInfoLog(fs, sizeof(log), &amp;log_size, log);
_asm int 3
}

prog = _glCreateProgram();
_glAttachShader(prog, vs);
_glAttachShader(prog, fs);
_glLinkProgram(prog);
_glGetProgramiv(prog, GL_LINK_STATUS, &amp;status);
if (status == GL_FALSE) {
char log[65536];
GLint log_size;
_glGetProgramInfoLog(prog, sizeof(log), &amp;log_size, log);
_asm int 3
}

_glUseProgram(prog);
i = _glGetUniformLocation(prog, "s0");
if (i < 0) _asm int 3
_glUniform1i(i, 0);
i = _glGetUniformLocation(prog, "s1");
if (i < 0) _asm int 3
_glUniform1i(i, 1);

// create texture and bind to sampler 0
glGenTextures(1, &amp;tex);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB5, 4, 4, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, image);

// bind the texture to sampler 1 too
_glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex);

_glGenSamplers(2, sam);
_glBindSampler(0, sam[0]);
_glBindSampler(1, sam[1]);
_glSamplerParameteri(sam[0], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
_glSamplerParameteri(sam[1], GL_TEXTURE_MAG_FILTER, GL_NEAREST);

_glGenBuffers(1, &amp;vb);
_glBindBuffer(GL_ARRAY_BUFFER, vb);
_glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
_glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(verts[0]), NULL);
_glEnableVertexAttribArray(0);
_glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(verts[0]), (char *)NULL + 2*sizeof(float));
_glEnableVertexAttribArray(1);

if (glGetError()) _asm int 3
}

int run;
LRESULT CALLBACK wnd_proc(HWND wnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_PAINT: ValidateRect(wnd, NULL); return 0;
case WM_CLOSE: run = 0; return 0;
default: return DefWindowProcA(wnd, msg, wp, lp);
}
}

int CALLBACK WinMain(HINSTANCE inst, HINSTANCE prev_inst, char *cl, int cs)
{
WNDCLASSA wc;
HWND wnd;
HDC dc;
PIXELFORMATDESCRIPTOR pfd;
int pf;
HGLRC rc;

memset(&amp;wc, 0, sizeof(wc));
wc.hInstance = inst;
wc.lpfnWndProc = wnd_proc;
wc.lpszClassName = "_test_class_name";
RegisterClassA(&amp;wc);
wnd = CreateWindowExA(0, wc.lpszClassName, NULL, WS_SYSMENU, 100, 100, 256, 196, NULL, NULL, NULL, NULL);
ShowWindow(wnd, SW_SHOW);
dc = GetDC(wnd);
memset(&amp;pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pdf);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DEPTH_DO NTCARE;
pf = ChoosePixelFormat(dc, &amp;pfd);
SetPixelFormat(dc, pf, NULL);
rc = wglCreateContext(dc);
wglMakeCurrent(dc, rc);
init();

run = 1;
while (run) {
MSG msg;
while (PeekMessageA(&amp;msg, NULL, 0, 0, PM_REMOVE))
DispatchMessageA(&amp;msg);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glFinish();
Sleep(100);
}

return 0;
}





by the way this is not a regression for 11.12, it is present in the older drivers too


I imagine this is some kind of optimization from the pre-sampler-object time which collapses samplers when they have the same texture bound and which was not updated to take into account the new sampler objects.

l_belev
01-21-2012, 04:39 PM
this bug is not present on nvidia. there the sample code works correctly - the red channel is filtered (linear) and the green is not (nearest).

ah, there is a minor compile error in the above code:
"pfd.nSize = sizeof(pdf);" should be "pfd.nSize = sizeof(pfd);" instead

Hongwei Li
01-24-2012, 02:45 AM
Oops, I was just about to do the same thing for my toy app, a filtering comparison demo. Let me check if I ran into the same problem. ;-)

l_belev
01-24-2012, 06:17 AM
Great! Can I take this as an indication the bug will be fixed soon? :)


Im joking. This is not really a showstopper bug, i think we can live with it. But still it would be nice to be fixed if its not too much trouble :)