AMD bug with samplers

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
"
		"layout(location=0) in vec2 a2v_pos;
"
		"layout(location=1) in vec2 a2v_tc;
"
		"out vec2 v2f_tc;
"
		"void main() {
"
		"	v2f_tc = a2v_tc;
"
		"	gl_Position = vec4(a2v_pos, 0, 1);
"
		"}";

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

	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 **)&_ ## 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, &txt, &len);
	_glCompileShader(vs);
	_glGetShaderiv(vs, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE) _asm int 3

	fs = _glCreateShader(GL_FRAGMENT_SHADER);
	txt = fs_text;
	len = sizeof(fs_text);
	_glShaderSource(fs, 1, &txt, &len);
	_glCompileShader(fs);
	_glGetShaderiv(fs, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE) {
		char log[65536];
		GLint log_size;
		_glGetShaderInfoLog(fs, sizeof(log), &log_size, log);
		_asm int 3
	}
	
	prog = _glCreateProgram();
	_glAttachShader(prog, vs);
	_glAttachShader(prog, fs);
	_glLinkProgram(prog);
	_glGetProgramiv(prog, GL_LINK_STATUS, &status);
	if (status == GL_FALSE) {
		char log[65536];
		GLint log_size;
		_glGetProgramInfoLog(prog, sizeof(log), &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, &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, &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(&wc, 0, sizeof(wc));
	wc.hInstance = inst;
	wc.lpfnWndProc = wnd_proc;
	wc.lpszClassName = "_test_class_name";
	RegisterClassA(&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(&pfd, 0, sizeof(pfd));
	pfd.nSize = sizeof(pdf);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DEPTH_DONTCARE;
	pf = ChoosePixelFormat(dc, &pfd);
	SetPixelFormat(dc, pf, NULL);
	rc = wglCreateContext(dc);
	wglMakeCurrent(dc, rc);
	init();

	run = 1;
	while (run) {
		MSG msg;
		while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE))
			DispatchMessageA(&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.

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

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. :wink:

Great! Can I take this as an indication the bug will be fixed soon? :slight_smile:

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 :slight_smile:

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.