libglvnd/tests/testgldispatch.c

444 lines
14 KiB
C

/*
* Copyright (c) 2017, NVIDIA CORPORATION.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and/or associated documentation files (the
* "Materials"), to deal in the Materials without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Materials, and to
* permit persons to whom the Materials are furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* unaltered in all copies or substantial portions of the Materials.
* Any additions, deletions, or changes to the original source files
* must be clearly indicated in accompanying documentation.
*
* THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <pthread.h>
#include <GL/gl.h>
#include <GLdispatch.h>
#include "dummy/patchentrypoints.h"
#define DUMMY_VENDOR_COUNT 3
#define NUM_GLDISPATCH_CALLS 2
static const char *GENERATED_FUNCTION_NAME = "glDummyTestGLVND";
enum {
CALL_INDEX_STATIC,
CALL_INDEX_GENERATED,
CALL_INDEX_STATIC_PATCH,
CALL_INDEX_GENERATED_PATCH,
CALL_INDEX_COUNT
};
typedef void (* pfn_glVertex3fv) (const GLfloat *v);
typedef struct DummyVendorLibRec {
pfn_glVertex3fv vertexProc;
pfn_glVertex3fv testProc;
__GLgetProcAddressCallback getProcCallback;
__GLdispatchThreadState threadState;
__GLdispatchTable *dispatch;
int vendorID;
const __GLdispatchPatchCallbacks *patchCallbacksPtr;
__GLdispatchPatchCallbacks patchCallbacks;
int callCounts[CALL_INDEX_COUNT];
} DummyVendorLib;
static void InitDummyVendors(void);
static void CleanupDummyVendors(void);
static void *ForceMultiThreadedProc(void *param);
static GLboolean TestDispatch(int vendorIndex,
GLboolean testStatic, GLboolean testGenerated);
static void *common_getProcAddressCallback(const char *procName, void *param, int vendorIndex);
static GLboolean common_InitiatePatch(int type, int stubSize,
DispatchPatchLookupStubOffset lookupStubOffset, int vendorIndex);
static void *dummy0_getProcAddressCallback(const char *procName, void *param);
static void dummy0_glVertex3fv(const GLfloat *v);
static void dummy0_glDummyTestProc(const GLfloat *v);
static GLboolean dummy0_InitiatePatch(int type, int stubSize,
DispatchPatchLookupStubOffset lookupStubOffset);
static void *dummy1_getProcAddressCallback(const char *procName, void *param);
static void dummy1_glVertex3fv(const GLfloat *v);
static void dummy1_glDummyTestProc(const GLfloat *v);
static GLboolean dummy1_InitiatePatch(int type, int stubSize,
DispatchPatchLookupStubOffset lookupStubOffset);
static void *dummy2_getProcAddressCallback(const char *procName, void *param);
static void dummy2_glVertex3fv(const GLfloat *v);
static void dummy2_glDummyTestProc(const GLfloat *v);
static DummyVendorLib dummyVendors[DUMMY_VENDOR_COUNT] = {
{ dummy0_glVertex3fv, dummy0_glDummyTestProc, dummy0_getProcAddressCallback },
{ dummy1_glVertex3fv, dummy1_glDummyTestProc, dummy1_getProcAddressCallback },
{ dummy2_glVertex3fv, dummy2_glDummyTestProc, dummy2_getProcAddressCallback },
};
static pfn_glVertex3fv ptr_glVertex3fv;
static pfn_glVertex3fv ptr_glDummyTestProc;
static GLboolean enableStaticTest = GL_FALSE;
static GLboolean enableGeneratedTest = GL_FALSE;
static GLboolean enablePatching = GL_FALSE;
static GLboolean forceMultiThreaded = GL_FALSE;
static GLboolean useLastGenerated = GL_FALSE;
int main(int argc, char **argv)
{
int i;
while (1) {
int opt = getopt(argc, argv, "sgptl");
if (opt == -1) {
break;
}
switch (opt) {
case 's':
enableStaticTest = GL_TRUE;
break;
case 'g':
enableGeneratedTest = GL_TRUE;
break;
case 'p':
enablePatching = GL_TRUE;
break;
case 't':
forceMultiThreaded = GL_TRUE;
break;
case 'l':
useLastGenerated = GL_TRUE;
break;
default:
return 1;
}
};
#if !defined(USE_DISPATCH_ASM)
// If the assembly dispatch stubs aren't enabled, then generating and
// patching entrypoints won't work. In that case, exit with 77 to tell
// automake to skip the test instead of failing.
if (enablePatching || enableGeneratedTest)
{
return 77;
}
#endif
#if !defined(GLDISPATCH_ENABLE_PATCHING)
if (enablePatching)
{
return 77;
}
#endif
__glDispatchInit();
InitDummyVendors();
if (forceMultiThreaded) {
pthread_t thr;
printf("Forcing libGLdispatch into multi-threaded mode.\n");
__glDispatchCheckMultithreaded();
pthread_create(&thr, NULL, ForceMultiThreadedProc, NULL);
pthread_join(thr, NULL);
}
ptr_glVertex3fv = (pfn_glVertex3fv) __glDispatchGetProcAddress("glVertex3fv");
if (ptr_glVertex3fv == NULL) {
printf("Can't find dispatch function for glVertex3fv\n");
}
if (enableGeneratedTest) {
if (useLastGenerated) {
// Get enough dispatch stubs so that the one we test is at the very
// end of the dispatch table. On some architectures, loading from a
// high index can be more complicated than a low index, so make
// sure we got it right.
for (i=0; i<4095; i++) {
char name[32];
snprintf(name, sizeof(name), "glDummyTestPaddingGLVND_%d", i);
__GLdispatchProc proc = __glDispatchGetProcAddress(name);
if (proc == NULL) {
printf("Can't find padding dispatch function for %d\n", i);
return 1;
}
}
}
ptr_glDummyTestProc = (pfn_glVertex3fv) __glDispatchGetProcAddress(GENERATED_FUNCTION_NAME);
if (ptr_glDummyTestProc == NULL) {
printf("Can't find dispatch function for %s\n", GENERATED_FUNCTION_NAME);
return 1;
}
if (useLastGenerated) {
// We should have reached the end of the dispatch table by now, so
// another __glDispatchGetProcAddress call should return NULL.
__GLdispatchProc proc = __glDispatchGetProcAddress("glDummyTestPaddingGLVND_last");
if (proc != NULL) {
printf("Got dispatch function past the end of the dispatch table.\n");
return 1;
}
}
}
for (i=0; i<DUMMY_VENDOR_COUNT; i++) {
if (!TestDispatch(i, enableStaticTest, enableGeneratedTest)) {
return 1;
}
}
CleanupDummyVendors();
__glDispatchFini();
return 0;
}
static void *ForceMultiThreadedProc(void *param)
{
__glDispatchCheckMultithreaded();
return NULL;
}
static void InitDummyVendors(void)
{
int i;
for (i=0; i<DUMMY_VENDOR_COUNT; i++) {
dummyVendors[i].vendorID = __glDispatchNewVendorID();
if (dummyVendors[i].vendorID == 0) {
printf("__glDispatchNewVendorID failed\n");
abort();
}
dummyVendors[i].dispatch = __glDispatchCreateTable(
dummyVendors[i].getProcCallback, &dummyVendors[i]);
if (dummyVendors[i].dispatch == NULL) {
printf("__glDispatchCreateTable failed\n");
abort();
}
}
if (enablePatching) {
dummyVendors[0].patchCallbacks.isPatchSupported = dummyCheckPatchSupported;
dummyVendors[0].patchCallbacks.initiatePatch = dummy0_InitiatePatch;
dummyVendors[0].patchCallbacksPtr = &dummyVendors[0].patchCallbacks;
dummyVendors[1].patchCallbacks.isPatchSupported = dummyCheckPatchSupported;
dummyVendors[1].patchCallbacks.initiatePatch = dummy1_InitiatePatch;
dummyVendors[1].patchCallbacksPtr = &dummyVendors[1].patchCallbacks;
}
}
static void CleanupDummyVendors(void)
{
int i;
for (i=0; i<DUMMY_VENDOR_COUNT; i++) {
if (dummyVendors[i].dispatch != NULL) {
__glDispatchDestroyTable(dummyVendors[i].dispatch);
dummyVendors[i].dispatch = NULL;
}
}
}
static void ResetCallCounts(void)
{
int i, j;
for (i=0; i<DUMMY_VENDOR_COUNT; i++) {
for (j=0; j<CALL_INDEX_COUNT; j++) {
dummyVendors[i].callCounts[j] = 0;
}
}
}
static GLboolean CheckCallCounts(int expectedVendorIndex, int expectedCallIndex, int count)
{
int vendorIndex, callIndex;
GLboolean result = GL_TRUE;
for (vendorIndex=0; vendorIndex<DUMMY_VENDOR_COUNT; vendorIndex++) {
for (callIndex=0; callIndex<CALL_INDEX_COUNT; callIndex++) {
int expected;
if (vendorIndex == expectedVendorIndex && callIndex == expectedCallIndex) {
expected = count;
} else {
expected = 0;
}
if (dummyVendors[vendorIndex].callCounts[callIndex] != expected) {
printf("Wrong value for vendor %d, call %d: Expected %d, got %d\n",
vendorIndex, callIndex, expected,
dummyVendors[vendorIndex].callCounts[callIndex]);
result = GL_FALSE;
}
}
}
return result;
}
static GLboolean TestDispatch(int vendorIndex,
GLboolean testStatic, GLboolean testGenerated)
{
int i;
GLboolean result = GL_FALSE;
GLboolean patched = (dummyVendors[vendorIndex].patchCallbacksPtr != NULL);
if (!__glDispatchMakeCurrent(&dummyVendors[vendorIndex].threadState,
dummyVendors[vendorIndex].dispatch, dummyVendors[vendorIndex].vendorID,
dummyVendors[vendorIndex].patchCallbacksPtr)) {
printf("__glDispatchMakeCurrent failed\n");
return GL_FALSE;
}
printf("Testing vendor %d, patched = %d\n", vendorIndex, (int) patched);
if (testStatic) {
int callIndex = (patched ? CALL_INDEX_STATIC_PATCH : CALL_INDEX_STATIC);
printf("Testing static dispatch through libOpenGL\n");
ResetCallCounts();
for (i = 0; i < NUM_GLDISPATCH_CALLS; i++) {
glVertex3fv(NULL);
}
if (!CheckCallCounts(vendorIndex, callIndex, NUM_GLDISPATCH_CALLS)) {
goto done;
}
printf("Testing static dispatch through GetProcAddress\n");
ResetCallCounts();
for (i = 0; i < NUM_GLDISPATCH_CALLS; i++) {
ptr_glVertex3fv(NULL);
}
if (!CheckCallCounts(vendorIndex, callIndex, NUM_GLDISPATCH_CALLS)) {
goto done;
}
}
if (testGenerated) {
int callIndex = (patched ? CALL_INDEX_GENERATED_PATCH : CALL_INDEX_GENERATED);
printf("Testing generated dispatch\n");
ResetCallCounts();
for (i = 0; i < NUM_GLDISPATCH_CALLS; i++) {
ptr_glDummyTestProc(NULL);
}
if (!CheckCallCounts(vendorIndex, callIndex, NUM_GLDISPATCH_CALLS)) {
goto done;
}
}
result = GL_TRUE;
done:
__glDispatchLoseCurrent();
return result;
}
static void *common_getProcAddressCallback(const char *procName, void *param, int vendorIndex)
{
DummyVendorLib *dummyVendor = (DummyVendorLib *) param;
if (dummyVendor != &dummyVendors[vendorIndex]) {
printf("getProcAddress for vendor %d called with the wrong parameter\n", vendorIndex);
abort();
}
if (strcmp(procName, "glVertex3fv") == 0) {
return dummyVendor->vertexProc;
} else if (strcmp(procName, GENERATED_FUNCTION_NAME) == 0) {
return dummyVendor->testProc;
} else {
return NULL;
}
}
static void *dummy0_getProcAddressCallback(const char *procName, void *param)
{
return common_getProcAddressCallback(procName, param, 0);
}
static void *dummy1_getProcAddressCallback(const char *procName, void *param)
{
return common_getProcAddressCallback(procName, param, 1);
}
static void *dummy2_getProcAddressCallback(const char *procName, void *param)
{
return common_getProcAddressCallback(procName, param, 2);
}
static void dummy0_glVertex3fv(const GLfloat *v)
{
dummyVendors[0].callCounts[CALL_INDEX_STATIC]++;
}
static void dummy1_glVertex3fv(const GLfloat *v)
{
dummyVendors[1].callCounts[CALL_INDEX_STATIC]++;
}
static void dummy2_glVertex3fv(const GLfloat *v)
{
dummyVendors[2].callCounts[CALL_INDEX_STATIC]++;
}
static void dummy0_glDummyTestProc(const GLfloat *v)
{
dummyVendors[0].callCounts[CALL_INDEX_GENERATED]++;
}
static void dummy1_glDummyTestProc(const GLfloat *v)
{
dummyVendors[1].callCounts[CALL_INDEX_GENERATED]++;
}
static void dummy2_glDummyTestProc(const GLfloat *v)
{
dummyVendors[2].callCounts[CALL_INDEX_GENERATED]++;
}
static GLboolean common_InitiatePatch(int type, int stubSize,
DispatchPatchLookupStubOffset lookupStubOffset, int vendorIndex)
{
if (!dummyPatchFunction(type, stubSize, lookupStubOffset, "Vertex3fv",
&dummyVendors[vendorIndex].callCounts[CALL_INDEX_STATIC_PATCH])) {
return GL_FALSE;
}
if (enableGeneratedTest) {
if (!dummyPatchFunction(type, stubSize, lookupStubOffset, GENERATED_FUNCTION_NAME,
&dummyVendors[vendorIndex].callCounts[CALL_INDEX_GENERATED_PATCH])) {
return GL_FALSE;
}
}
return GL_TRUE;
}
static GLboolean dummy0_InitiatePatch(int type, int stubSize,
DispatchPatchLookupStubOffset lookupStubOffset)
{
return common_InitiatePatch(type, stubSize, lookupStubOffset, 0);
}
static GLboolean dummy1_InitiatePatch(int type, int stubSize,
DispatchPatchLookupStubOffset lookupStubOffset)
{
return common_InitiatePatch(type, stubSize, lookupStubOffset, 1);
}