From: Rémi Bernon <rbernon@codeweavers.com> --- dlls/opengl32/tests/opengl.c | 444 ++++++++++++++++++++++++++++++++++- 1 file changed, 443 insertions(+), 1 deletion(-) diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c index a8df849195c..c45294b134f 100644 --- a/dlls/opengl32/tests/opengl.c +++ b/dlls/opengl32/tests/opengl.c @@ -56,8 +56,10 @@ static const char *debugstr_ok( const char *cond ) t v = (r); \ ok( v op (e), "%s " f "\n", debugstr_ok( #r ), v, ##__VA_ARGS__ ); \ } while (0) +#define ok_u4( r, op, e ) ok_ex( r, op, e, UINT, "%u" ) #define ok_ptr( r, op, e ) ok_ex( r, op, e, const void *, "%p" ) #define ok_ret( e, r ) ok_ex( r, ==, e, UINT_PTR, "%#Ix, error %ld", GetLastError() ) +#define ok_nt( e, r ) ok_ex( r, ==, e, NTSTATUS, "%#lx" ) #define check_gl_error(exp) check_gl_error_(__LINE__, exp) static void check_gl_error_( unsigned int line, GLenum exp ) @@ -1052,6 +1054,442 @@ static void test_setpixelformat(HDC winhdc) } } +enum object_type +{ + OBJ_BUFFER, + OBJ_BUFFER_ARB, + OBJ_COMMAND_LIST_NV, + OBJ_FENCE_APPLE, + OBJ_FENCE_NV, + OBJ_FRAMEBUFFER, + OBJ_FRAMEBUFFER_EXT, + OBJ_DISPLAY_LIST, + OBJ_MEMORY_OBJECT_EXT, + OBJ_OBJECT_BUFFER_ATI, + OBJ_PATH_NV, + OBJ_PROGRAM_ARB, + OBJ_PROGRAM_NV, + OBJ_SHADER_EXT, + OBJ_SHADER_ATI, + OBJ_PROGRAM_OBJECT, + OBJ_PROGRAM_OBJECT_ARB, + OBJ_SHADER_OBJECT, + OBJ_SHADER_OBJECT_ARB, + OBJ_PROGRAM_PIPELINE, + OBJ_QUERY, + OBJ_QUERY_ARB, + OBJ_OCCLUSION_QUERY_NV, + OBJ_RENDERBUFFER, + OBJ_RENDERBUFFER_EXT, + OBJ_SAMPLER, + OBJ_SEMAPHORE_EXT, + OBJ_STATE_NV, + OBJ_TEXTURE, + OBJ_TEXTURE_EXT, + OBJ_TRANSFORM_FEEDBACK, + OBJ_TRANSFORM_FEEDBACK_NV, + OBJ_VERTEX_ARRAY, + OBJ_VERTEX_ARRAY_APPLE, + OBJ_TYPE_COUNT, +}; + +static const char *debugstr_object_type( enum object_type type ) +{ + switch (type) + { + case OBJ_BUFFER: return "buffer"; + case OBJ_BUFFER_ARB: return "buffer_arb"; + case OBJ_COMMAND_LIST_NV: return "command_list_nv"; + case OBJ_FENCE_APPLE: return "fence_apple"; + case OBJ_FENCE_NV: return "fence_nv"; + case OBJ_FRAMEBUFFER: return "framebuffer"; + case OBJ_FRAMEBUFFER_EXT: return "framebuffer_ext"; + case OBJ_DISPLAY_LIST: return "display list"; + case OBJ_MEMORY_OBJECT_EXT: return "memory_object_ext"; + case OBJ_OBJECT_BUFFER_ATI: return "object_buffer_ati"; + case OBJ_PATH_NV: return "path_nv"; + case OBJ_PROGRAM_ARB: return "program_arb"; + case OBJ_PROGRAM_NV: return "program_nv"; + case OBJ_SHADER_EXT: return "shader_ext"; + case OBJ_SHADER_ATI: return "shader_ati"; + case OBJ_PROGRAM_OBJECT: return "program"; + case OBJ_PROGRAM_OBJECT_ARB: return "program_object_arb"; + case OBJ_SHADER_OBJECT: return "shader"; + case OBJ_SHADER_OBJECT_ARB: return "shader_object_arb"; + case OBJ_PROGRAM_PIPELINE: return "program_pipeline"; + case OBJ_QUERY: return "query"; + case OBJ_QUERY_ARB: return "query_arb"; + case OBJ_OCCLUSION_QUERY_NV: return "occlusion_query_nv"; + case OBJ_RENDERBUFFER: return "renderbuffer"; + case OBJ_RENDERBUFFER_EXT: return "renderbuffer_ext"; + case OBJ_SAMPLER: return "sampler"; + case OBJ_SEMAPHORE_EXT: return "semaphore_ext"; + case OBJ_STATE_NV: return "state_nv"; + case OBJ_TEXTURE: return "texture"; + case OBJ_TEXTURE_EXT: return "texture_ext"; + case OBJ_TRANSFORM_FEEDBACK: return "transform_feedback"; + case OBJ_TRANSFORM_FEEDBACK_NV: return "transform_feedback_nv"; + case OBJ_VERTEX_ARRAY: return "vertex_array"; + case OBJ_VERTEX_ARRAY_APPLE: return "vertex_array_apple"; + default: return wine_dbg_sprintf( "%u", type ); + } +} + +static BOOL create_object( enum object_type type, GLuint name, GLuint *obj ) +{ + switch (type) + { + case OBJ_BUFFER: + if (!pglGenBuffers) return FALSE; + if (!(*obj = name)) pglGenBuffers( 1, obj ); + pglBindBuffer( GL_ARRAY_BUFFER, *obj ); + break; + case OBJ_BUFFER_ARB: + if (!pglGenBuffersARB) return FALSE; + if (!(*obj = name)) pglGenBuffersARB( 1, obj ); + pglBindBufferARB( GL_ARRAY_BUFFER, *obj ); + break; + case OBJ_COMMAND_LIST_NV: + if (!pglCreateCommandListsNV) return FALSE; + pglCreateCommandListsNV( 1, obj ); + break; + case OBJ_FENCE_APPLE: + if (!pglGenFencesAPPLE) return FALSE; + if (!(*obj = name)) pglGenFencesAPPLE( 1, obj ); + pglSetFenceAPPLE( *obj ); break; + case OBJ_FENCE_NV: + if (!pglGenFencesNV) return FALSE; + if (!(*obj = name)) pglGenFencesNV( 1, obj ); + pglSetFenceNV( *obj, GL_ALL_COMPLETED_NV ); break; + case OBJ_FRAMEBUFFER: + if (!pglGenFramebuffers) return FALSE; + if (!(*obj = name)) pglGenFramebuffers( 1, obj ); + pglBindFramebuffer( GL_DRAW_FRAMEBUFFER, *obj ); + break; + case OBJ_FRAMEBUFFER_EXT: + if (!pglGenFramebuffersEXT) return FALSE; + if (!(*obj = name)) pglGenFramebuffersEXT( 1, obj ); + pglBindFramebufferEXT( GL_DRAW_FRAMEBUFFER, *obj ); + break; + case OBJ_DISPLAY_LIST: + if (!(*obj = name)) *obj = glGenLists( 1 ); + glNewList( *obj, GL_COMPILE ); + glClear( GL_COLOR_BUFFER_BIT ); + glEndList(); + break; + case OBJ_MEMORY_OBJECT_EXT: + if (!pglCreateMemoryObjectsEXT) return FALSE; + pglCreateMemoryObjectsEXT( 1, obj ); + break; + case OBJ_OBJECT_BUFFER_ATI: + if (!pglNewObjectBufferATI) return FALSE; + *obj = pglNewObjectBufferATI( sizeof(name), &name, GL_STATIC_ATI ); + break; + case OBJ_PATH_NV: + { + static const GLshort coords[2] = {100, 180}; + static const GLubyte cmds[1] = {GL_MOVE_TO_NV}; + + if (!pglGenPathsNV) return FALSE; + if (!(*obj = name)) *obj = pglGenPathsNV( 1 ); + pglPathCommandsNV( *obj, 1, cmds, 2, GL_SHORT, coords ); + break; + } + case OBJ_PROGRAM_ARB: + { + static const GLubyte shader[] = "!!ARBfp1.0\nEND"; + + if (!pglGenProgramsARB) return FALSE; + if (!(*obj = name)) pglGenProgramsARB( 1, obj ); + pglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, *obj ); + pglProgramStringARB( GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, sizeof(shader) - 1, shader); + break; + } + case OBJ_PROGRAM_NV: + { + static const GLubyte shader[] = "!!VP1.0END"; + + if (!pglGenProgramsNV) return FALSE; + if (!(*obj = name)) pglGenProgramsNV( 1, obj ); + pglBindProgramNV( GL_VERTEX_PROGRAM_NV, *obj ); + pglLoadProgramNV( GL_VERTEX_PROGRAM_NV, *obj, sizeof(shader) - 1, shader); + break; + } + case OBJ_SHADER_EXT: + if (!pglGenVertexShadersEXT) return FALSE; + if (!(*obj = name)) *obj = pglGenVertexShadersEXT( 1 ); + pglBindVertexShaderEXT( *obj ); + break; + case OBJ_SHADER_ATI: + if (!pglGenFragmentShadersATI) return FALSE; + if (!(*obj = name)) *obj = pglGenFragmentShadersATI( 1 ); + pglBindFragmentShaderATI( *obj ); + break; + case OBJ_PROGRAM_OBJECT: + if (!pglCreateProgram) return FALSE; + *obj = pglCreateProgram(); + break; + case OBJ_PROGRAM_OBJECT_ARB: + if (!pglCreateProgramObjectARB) return FALSE; + *obj = pglCreateProgramObjectARB(); + break; + case OBJ_SHADER_OBJECT: + if (!pglCreateShader) return FALSE; + *obj = pglCreateShader( GL_VERTEX_SHADER ); + break; + case OBJ_SHADER_OBJECT_ARB: + if (!pglCreateShaderObjectARB) return FALSE; + *obj = pglCreateShaderObjectARB( GL_VERTEX_SHADER_ARB ); + break; + case OBJ_PROGRAM_PIPELINE: + if (!pglGenProgramPipelines) return FALSE; + if (!(*obj = name)) pglGenProgramPipelines( 1, obj ); + pglBindProgramPipeline( *obj ); + break; + case OBJ_QUERY: + if (!pglGenQueries) return FALSE; + if (!(*obj = name)) pglGenQueries( 1, obj ); + pglBeginQuery( GL_SAMPLES_PASSED, *obj ); + pglEndQuery( GL_SAMPLES_PASSED ); + break; + case OBJ_QUERY_ARB: + if (!pglGenQueriesARB) return FALSE; + if (!(*obj = name)) pglGenQueriesARB( 1, obj ); + pglBeginQueryARB( GL_SAMPLES_PASSED_ARB, *obj ); + pglEndQueryARB( GL_SAMPLES_PASSED_ARB ); + break; + case OBJ_OCCLUSION_QUERY_NV: + if (!pglGenOcclusionQueriesNV) return FALSE; + if (!(*obj = name)) pglGenOcclusionQueriesNV( 1, obj ); + pglBeginOcclusionQueryNV( *obj ); + pglEndOcclusionQueryNV(); + break; + case OBJ_RENDERBUFFER: + if (!pglGenRenderbuffers) return FALSE; + if (!(*obj = name)) pglGenRenderbuffers( 1, obj ); + pglBindRenderbuffer( GL_RENDERBUFFER, *obj ); + break; + case OBJ_RENDERBUFFER_EXT: + if (!pglGenRenderbuffersEXT) return FALSE; + if (!(*obj = name)) pglGenRenderbuffersEXT( 1, obj ); + pglBindRenderbufferEXT( GL_RENDERBUFFER_EXT, *obj ); + break; + case OBJ_SAMPLER: + if (!pglGenSamplers) return FALSE; + if (!(*obj = name)) pglGenSamplers( 1, obj ); + pglBindSampler( 0, *obj ); + break; + case OBJ_SEMAPHORE_EXT: + { + D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter = {0}; + D3DKMT_DESTROYSYNCHRONIZATIONOBJECT destroy = {0}; + D3DKMT_CREATESYNCHRONIZATIONOBJECT2 create2 = {0}; + D3DKMT_DESTROYDEVICE destroy_device = {0}; + D3DKMT_CREATEDEVICE create_device = {0}; + D3DKMT_CLOSEADAPTER close_adapter = {0}; + NTSTATUS status; + + if (!pglGenSemaphoresEXT) return FALSE; + + wcscpy( open_adapter.DeviceName, L"\\\\.\\DISPLAY1" ); + status = D3DKMTOpenAdapterFromGdiDisplayName( &open_adapter ); + ok_nt( STATUS_SUCCESS, status ); + create_device.hAdapter = open_adapter.hAdapter; + status = D3DKMTCreateDevice( &create_device ); + ok_nt( STATUS_SUCCESS, status ); + + create2.hDevice = create_device.hDevice; + create2.Info.Type = D3DDDI_FENCE; + create2.Info.Flags.Shared = 1; + create2.hSyncObject = create2.Info.SharedHandle = 0x1eadbeed; + status = D3DKMTCreateSynchronizationObject2( &create2 ); + ok_nt( STATUS_SUCCESS, status ); + + if (!(*obj = name)) pglGenSemaphoresEXT( 1, obj ); + pglImportSemaphoreWin32HandleEXT( *obj, GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT, + UlongToHandle( create2.Info.SharedHandle ) ); + + destroy.hSyncObject = create2.hSyncObject; + status = D3DKMTDestroySynchronizationObject( &destroy ); + ok_nt( STATUS_SUCCESS, status ); + destroy_device.hDevice = create_device.hDevice; + status = D3DKMTDestroyDevice( &destroy_device ); + ok_nt( STATUS_SUCCESS, status ); + close_adapter.hAdapter = open_adapter.hAdapter; + status = D3DKMTCloseAdapter( &close_adapter ); + ok_nt( STATUS_SUCCESS, status ); + break; + } + case OBJ_STATE_NV: + if (!pglCreateStatesNV) return FALSE; + pglCreateStatesNV( 1, obj ); + break; + case OBJ_TEXTURE: + if (!(*obj = name)) glGenTextures( 1, obj ); + glBindTexture( GL_TEXTURE_2D, *obj ); + break; + case OBJ_TEXTURE_EXT: + if (!pglGenTexturesEXT) return FALSE; + if (!(*obj = name)) pglGenTexturesEXT( 1, obj ); + pglBindTextureEXT( GL_TEXTURE_2D, *obj ); + break; + case OBJ_TRANSFORM_FEEDBACK: + if (!pglGenTransformFeedbacks) return FALSE; + if (!(*obj = name)) pglGenTransformFeedbacks( 1, obj ); + pglBindTransformFeedback( GL_TRANSFORM_FEEDBACK, *obj ); + break; + case OBJ_TRANSFORM_FEEDBACK_NV: + if (!pglGenTransformFeedbacksNV) return FALSE; + if (!(*obj = name)) pglGenTransformFeedbacksNV( 1, obj ); + pglBindTransformFeedbackNV( GL_TRANSFORM_FEEDBACK_NV, *obj ); + break; + case OBJ_VERTEX_ARRAY: + if (!pglGenVertexArrays) return FALSE; + if (!(*obj = name)) pglGenVertexArrays( 1, obj ); + pglBindVertexArray( *obj ); + break; + case OBJ_VERTEX_ARRAY_APPLE: + if (!pglGenVertexArraysAPPLE) return FALSE; + if (!(*obj = name)) pglGenVertexArraysAPPLE( 1, obj ); + pglBindVertexArrayAPPLE( *obj ); + break; + case OBJ_TYPE_COUNT: return FALSE; + } + + return TRUE; +} + +/* some functions don't allow implicit names even in compat contexts, or even in core contexts */ +static BOOL is_implicit_allowed( enum object_type type, BOOL compat ) +{ + switch (type) + { + case OBJ_BUFFER: return compat; + case OBJ_BUFFER_ARB: return compat; + case OBJ_DISPLAY_LIST: return compat; + case OBJ_OCCLUSION_QUERY_NV: return compat; + case OBJ_QUERY: return compat; + case OBJ_QUERY_ARB: return compat; + case OBJ_TEXTURE: return compat; + case OBJ_TEXTURE_EXT: return compat; + + /* never allow implicit allocation even in compat contexts */ + case OBJ_FRAMEBUFFER: return FALSE; + case OBJ_PROGRAM_PIPELINE: return FALSE; + case OBJ_RENDERBUFFER: return FALSE; + case OBJ_SAMPLER: return FALSE; + case OBJ_TRANSFORM_FEEDBACK: return FALSE; + case OBJ_VERTEX_ARRAY: return FALSE; + + /* always allow implicit allocation even in core contexts */ + case OBJ_FENCE_APPLE: return TRUE; + case OBJ_FENCE_NV: return TRUE; + case OBJ_FRAMEBUFFER_EXT: return TRUE; + case OBJ_PATH_NV: return TRUE; + case OBJ_PROGRAM_ARB: return TRUE; + case OBJ_PROGRAM_NV: return TRUE; + case OBJ_RENDERBUFFER_EXT: return TRUE; + case OBJ_SEMAPHORE_EXT: return TRUE; + case OBJ_SHADER_ATI: return TRUE; + case OBJ_SHADER_EXT: return TRUE; + case OBJ_TRANSFORM_FEEDBACK_NV: return TRUE; + case OBJ_VERTEX_ARRAY_APPLE: return TRUE; + + /* some types are always allocated explicitly */ + case OBJ_COMMAND_LIST_NV: return TRUE; + case OBJ_MEMORY_OBJECT_EXT: return TRUE; + case OBJ_OBJECT_BUFFER_ATI: return TRUE; + case OBJ_PROGRAM_OBJECT: return TRUE; + case OBJ_PROGRAM_OBJECT_ARB: return TRUE; + case OBJ_SHADER_OBJECT: return TRUE; + case OBJ_SHADER_OBJECT_ARB: return TRUE; + case OBJ_STATE_NV: return TRUE; + + default: return FALSE; + } +} + +static void test_object_creation(HDC winhdc) +{ + static const GLint compat_attribs[] = + { + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0, 0 + }; + static const GLint core_attribs[] = + { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, 0 + }; + + GLuint obj; + HGLRC ctx; + + for (UINT i = 0; i < OBJ_TYPE_COUNT; i++) + { + if (broken( i == OBJ_TRANSFORM_FEEDBACK )) continue; /* NVIDIA / AMD don't agree */ + + winetest_push_context( "%u %s compat", i, debugstr_object_type( i ) ); + + ctx = pwglCreateContextAttribsARB( winhdc, NULL, compat_attribs ); + ok_ptr( ctx, !=, NULL ); + ok_ret( TRUE, wglMakeCurrent( winhdc, ctx ) ); + ok_ret( GL_NO_ERROR, glGetError() ); + + if (!create_object( i, 1, &obj )) + { + skip( "Skipping unsupported object type.\n" ); + goto next; + } + if (!is_implicit_allowed( i, TRUE )) + { + todo_wine_if( i == OBJ_FRAMEBUFFER || i == OBJ_RENDERBUFFER ) + ok_ret( GL_INVALID_OPERATION, glGetError() ); + if (!winetest_platform_is_wine || (i != OBJ_FRAMEBUFFER && i != OBJ_RENDERBUFFER)) + ok_ret( TRUE, create_object( i, 0, &obj ) ); + } + ok_ret( GL_NO_ERROR, glGetError() ); + ok_u4( obj, ==, 1 ); + ok_ret( GL_NO_ERROR, glGetError() ); + + ok_ret( TRUE, wglDeleteContext( ctx ) ); + + winetest_pop_context(); + + + winetest_push_context( "%u %s core", i, debugstr_object_type( i ) ); + + ctx = pwglCreateContextAttribsARB( winhdc, NULL, core_attribs ); + ok_ptr( ctx, !=, NULL ); + ok_ret( TRUE, wglMakeCurrent( winhdc, ctx ) ); + ok_ret( GL_NO_ERROR, glGetError() ); + + ok_ret( TRUE, create_object( i, 1, &obj ) ); + if (!is_implicit_allowed( i, FALSE )) + { + ok_ret( GL_INVALID_OPERATION, glGetError() ); + ok_ret( TRUE, create_object( i, 0, &obj ) ); + } + if (i == OBJ_DISPLAY_LIST) ok_ret( GL_INVALID_OPERATION, glGetError() ); + else + { + /* Wine never allows implicit allocation in core contexts */ + todo_wine_if( i == OBJ_FENCE_APPLE || i == OBJ_FENCE_NV || i == OBJ_FRAMEBUFFER_EXT || i == OBJ_PATH_NV || + i == OBJ_PROGRAM_ARB || i == OBJ_PROGRAM_NV || i == OBJ_SHADER_EXT || i == OBJ_SHADER_ATI || + i == OBJ_RENDERBUFFER_EXT || i == OBJ_SEMAPHORE_EXT || i == OBJ_TRANSFORM_FEEDBACK_NV || + i == OBJ_VERTEX_ARRAY_APPLE ) + ok_ret( GL_NO_ERROR, glGetError() ); + ok_u4( obj, ==, 1 ); + } + +next: + ok_ret( TRUE, wglDeleteContext( ctx ) ); + winetest_pop_context(); + } +} + static void test_sharelists(HDC winhdc) { BOOL res, nvidia, amd, source_current, source_sharing, dest_current, dest_sharing; @@ -3865,7 +4303,11 @@ START_TEST(opengl) if (wgl_extensions == NULL) skip( "Skipping opengl32 tests because this OpenGL implementation " "doesn't support WGL extensions!\n" ); - if (strstr( wgl_extensions, "WGL_ARB_create_context" )) test_opengl3( hdc ); + if (strstr( wgl_extensions, "WGL_ARB_create_context" )) + { + test_opengl3( hdc ); + test_object_creation( hdc ); + } if (strstr( wgl_extensions, "WGL_ARB_make_current_read" )) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10739