| #include "stdlib.h" | |
| #include "radfnptrinit.h" | |
| #include "string.h" | |
| #ifdef _WIN32 | |
| #include <windows.h> | |
| #endif | |
| #ifdef __linux__ | |
| #include <GL/glx.h> | |
| #endif | |
| #include <stdio.h> | |
| #include <time.h> | |
| #include <RAD/rad.h> | |
| #include <EGL/egl.h> | |
| RADdevice device; | |
| RADqueue queue; | |
| bool useCopyQueue = false /*true*/; | |
| RADqueue copyQueue; | |
| #define SHADER_SOURCE(source) #source | |
| static const char *vsstring = | |
| //"#version 440 core\n" | |
| //"#define BINDGROUP(GROUP,INDEX) layout(binding = ((INDEX) | ((GROUP) << 4)))\n" | |
| //"layout(location = 1) in vec4 tc;\n" | |
| //"layout(location = 0) in vec4 position;\n" | |
| //"BINDGROUP(0, 2) uniform Block {\n" | |
| //" vec4 scale;\n" | |
| //"};\n" | |
| //"out vec4 ftc;\n" | |
| //"void main() {\n" | |
| //" gl_Position = position*scale;\n" | |
| //// This line exists to trick the compiler into putting a value in the compiler | |
| //// constant bank, so we can exercise binding that bank | |
| //" if (scale.z != 1.0 + 1.0/65536.0) {\n" | |
| //" gl_Position = vec4(0,0,0,0);\n" | |
| //" }\n" | |
| //" ftc = tc;\n" | |
| //"}\n"; | |
| SHADER_SOURCE(attribute highp vec4 position; | |
| attribute highp vec4 tc; | |
| varying highp vec4 ftc; | |
| uniform highp vec4 scale; | |
| void main(void) | |
| { | |
| gl_Position = position * scale; | |
| ftc = tc; | |
| }); | |
| static const char *fsstring = | |
| /* "#version 440 core\n" | |
| "#define BINDGROUP(GROUP,INDEX) layout(binding = ((INDEX) | ((GROUP) << 4)))\n" | |
| "BINDGROUP(0, 3) uniform sampler2D tex;\n" | |
| "BINDGROUP(0, 2) uniform Block {\n" | |
| " vec4 scale;\n" | |
| "};\n" | |
| "layout(location = 0) out vec4 color;\n" | |
| "in vec4 ftc;\n" | |
| "void main() {\n" | |
| " color = texture(tex, ftc.xy);\n" | |
| " if (scale.z != 1.0 + 1.0/65536.0) {\n" | |
| " color = vec4(0,0,0,0);\n" | |
| " }\n" | |
| "}\n";*/ | |
| SHADER_SOURCE( | |
| varying highp vec4 ftc; | |
| uniform sampler2D tex; | |
| void main (void) | |
| { | |
| gl_FragColor = texture2D(tex, ftc.xy);\n | |
| }); | |
| // Two triangles that intersect | |
| static RADfloat vertexData[] = {-0.5f, -0.5f, 0.5f, | |
| 0.5f, -0.5f, 0.5f, | |
| -0.5f, 0.5f, 0.5f, | |
| 0.5f, -0.5f, 0.5f, | |
| -0.5f, -0.5f, 0.3f, | |
| 0.5f, 0.5f, 0.9f}; | |
| // Simple 0/1 texcoords in rgba8 format (used to be color data) | |
| static RADubyte texcoordData[] = {0, 0, 0xFF, 0xFF, | |
| 0xFF, 0, 0, 0xFF, | |
| 0, 0xFF, 0, 0xFF, | |
| 0, 0, 0xFF, 0xFF, | |
| 0xFF, 0, 0, 0xFF, | |
| 0, 0xFF, 0, 0xFF}; | |
| int windowWidth = 500, windowHeight = 500; | |
| int offscreenWidth = 500, offscreenHeight = 500; | |
| typedef enum { | |
| QUEUE, | |
| TOKEN, | |
| COMMAND, | |
| } DrawMode; | |
| DrawMode drawMode = QUEUE /* COMMAND */; | |
| bool benchmark = false; | |
| void InitRAD() | |
| { | |
| PFNRADGETPROCADDRESSPROC getProc = (PFNRADGETPROCADDRESSPROC)eglGetProcAddress("radGetProcAddress"); | |
| radLoadProcs(getProc); | |
| // Create the "global" device and one queue | |
| device = radCreateDevice(); | |
| queue = radCreateQueue(device, RAD_QUEUE_TYPE_GRAPHICS_AND_COMPUTE); | |
| if (useCopyQueue) { | |
| copyQueue = radCreateQueue(device, RAD_QUEUE_TYPE_GRAPHICS_AND_COMPUTE); | |
| } | |
| } | |
| void CleanRAD() | |
| { | |
| if (useCopyQueue) { | |
| radReleaseQueue(copyQueue); | |
| } | |
| radReleaseQueue(queue); | |
| radReleaseDevice(device); | |
| } | |
| #define USE_MULTISAMPLE 0 | |
| RADbuffer AllocAndFillBuffer(RADdevice device, void *data, int sizeofdata, RADbitfield access, bool useCopy) | |
| { | |
| if (useCopy) { | |
| RADbuffer tempbo = radCreateBuffer(device); | |
| radBufferAccess(tempbo, RAD_COPY_READ_ACCESS_BIT); | |
| radBufferMapAccess(tempbo, RAD_MAP_WRITE_BIT | RAD_MAP_PERSISTENT_BIT); | |
| radBufferStorage(tempbo, sizeofdata); | |
| void *ptr = radMapBuffer(tempbo); | |
| memcpy(ptr, data, sizeofdata); | |
| RADbuffer buffer = radCreateBuffer(device); | |
| radBufferAccess(buffer, access | RAD_COPY_WRITE_ACCESS_BIT); | |
| radBufferMapAccess(buffer, 0); | |
| radBufferStorage(buffer, sizeofdata); | |
| radQueueCopyBuffer(useCopyQueue ? copyQueue : queue, tempbo, 0, buffer, 0, sizeofdata); | |
| radReleaseBuffer(tempbo, RAD_TAG_AUTO); | |
| return buffer; | |
| } else { | |
| RADbuffer buffer = radCreateBuffer(device); | |
| radBufferAccess(buffer, access); | |
| radBufferMapAccess(buffer, RAD_MAP_WRITE_BIT | RAD_MAP_PERSISTENT_BIT); | |
| radBufferStorage(buffer, sizeofdata); | |
| void *ptr = radMapBuffer(buffer); | |
| memcpy(ptr, data, sizeofdata); | |
| return buffer; | |
| } | |
| } | |
| void TestRAD() | |
| { | |
| // Create programs from the device, provide them shader code and compile/link them | |
| RADprogram pgm = radCreateProgram(device); | |
| // XXX This is a hack because we don't have an IL. I'm just jamming through the strings | |
| // as if they were an IL blob | |
| const char *source[2] = {vsstring, fsstring}; | |
| radProgramSource(pgm, RAD_PROGRAM_FORMAT_GLSL, /*size*/2, (void *)source); | |
| // Create new state vectors | |
| RADcolorState color = radCreateColorState(device); | |
| RADdepthStencilState depth = radCreateDepthStencilState(device); | |
| RADvertexState vertex = radCreateVertexState(device); | |
| RADrasterState raster = radCreateRasterState(device); | |
| RADrtFormatState rtFormat = radCreateRtFormatState(device); | |
| radRtFormatColorFormat(rtFormat, 0, RAD_RGBA8); | |
| radRtFormatDepthFormat(rtFormat, RAD_DEPTH24_STENCIL8); | |
| radDepthStencilDepthTestEnable(depth, RAD_TRUE); | |
| radDepthStencilDepthWriteEnable(depth, RAD_TRUE); | |
| radDepthStencilDepthFunc(depth, RAD_DEPTH_FUNC_LESS); | |
| // Commented out experiments to test different state settings. | |
| //radRasterDiscardEnable(raster, RAD_TRUE); | |
| //radRasterPolygonMode(raster, RAD_POLYGON_MODE_LINE); | |
| //radRasterLineWidth(raster, 10.0f); | |
| //radColorBlendEnable(color, /*MRT index*/0, RAD_TRUE); | |
| //radColorBlendFunc(color, /*MRT index*/0, RAD_BLEND_FUNC_ONE, RAD_BLEND_FUNC_ONE, RAD_BLEND_FUNC_ONE, RAD_BLEND_FUNC_ONE); | |
| //radColorMask(color, /*MRT index*/0, RAD_TRUE, RAD_TRUE, RAD_TRUE, RAD_TRUE); | |
| //radColorLogicOpEnable(color, RAD_TRUE); | |
| //radColorLogicOp(color, RAD_LOGIC_OP_XOR); | |
| // Set the state vector to use two vertex attributes. | |
| // | |
| // Interleaved pos+color | |
| // position = attrib 0 = 3*float at relativeoffset 0 | |
| // texcoord = attrib 1 = rgba8 at relativeoffset 0 | |
| radVertexAttribFormat(vertex, 0, 3, sizeof(RADfloat), RAD_ATTRIB_TYPE_FLOAT, 0); | |
| radVertexAttribFormat(vertex, 1, 4, sizeof(RADubyte), RAD_ATTRIB_TYPE_UNORM, 0); | |
| radVertexAttribBinding(vertex, 0, 0); | |
| radVertexAttribBinding(vertex, 1, 1); | |
| radVertexBindingGroup(vertex, 0, /*group*/0, /*index*/0); | |
| radVertexBindingGroup(vertex, 1, /*group*/0, /*index*/1); | |
| radVertexAttribEnable(vertex, 0, RAD_TRUE); | |
| radVertexAttribEnable(vertex, 1, RAD_TRUE); | |
| radVertexBindingStride(vertex, 0, 12); | |
| radVertexBindingStride(vertex, 1, 4); | |
| // Create a pipeline. | |
| RADpipeline pipeline = radCreatePipeline(device, RAD_PIPELINE_TYPE_GRAPHICS); | |
| // Attach program and state objects to the pipeline. | |
| radPipelineProgramStages(pipeline, RAD_VERTEX_SHADER_BIT | RAD_FRAGMENT_SHADER_BIT, pgm); | |
| radPipelineColorState(pipeline, color); | |
| radPipelineDepthStencilState(pipeline, depth); | |
| radPipelineVertexState(pipeline, vertex); | |
| radPipelineRasterState(pipeline, raster); | |
| radPipelineRtFormatState(pipeline, rtFormat); | |
| radPipelinePrimitiveType(pipeline, RAD_TRIANGLES); | |
| // Create a vertex buffer and fill it with data | |
| RADbuffer vbo = radCreateBuffer(device); | |
| radBufferAccess(vbo, RAD_VERTEX_ACCESS_BIT); | |
| radBufferMapAccess(vbo, RAD_MAP_WRITE_BIT | RAD_MAP_PERSISTENT_BIT); | |
| radBufferStorage(vbo, sizeof(vertexData)+sizeof(texcoordData)); | |
| // create persistent mapping | |
| void *ptr = radMapBuffer(vbo); | |
| // fill ptr with vertex data followed by color data | |
| memcpy(ptr, vertexData, sizeof(vertexData)); | |
| memcpy((char *)ptr + sizeof(vertexData), texcoordData, sizeof(texcoordData)); | |
| // Get a handle to be used for setting the buffer as a vertex buffer | |
| RADvertexHandle vboHandle = radGetVertexHandle(vbo); | |
| // Create an index buffer and fill it with data | |
| unsigned short indexData[6] = {0, 1, 2, 3, 4, 5}; | |
| RADbuffer ibo = AllocAndFillBuffer(device, indexData, sizeof(indexData), RAD_INDEX_ACCESS_BIT, false /*true*/); | |
| // Get a handle to be used for setting the buffer as an index buffer | |
| RADvertexHandle iboHandle = radGetVertexHandle(ibo); | |
| float scale = 1.5f; | |
| if (benchmark) { | |
| scale = 0.2f; | |
| } | |
| float uboData[4] = {scale, scale, 1.0f + 1.0f/65536.0, 1.0f}; | |
| RADbuffer ubo = AllocAndFillBuffer(device, uboData, sizeof(uboData), RAD_UNIFORM_ACCESS_BIT, false); | |
| // Get a handle to be used for setting the buffer as a uniform buffer | |
| RADuniformHandle uboHandle = radGetUniformHandle(ubo); | |
| #if USE_MULTISAMPLE | |
| RADtexture rtTex = radCreateTexture(device); | |
| radTextureAccess(rtTex, RAD_RENDER_TARGET_ACCESS_BIT); | |
| radTextureStorage(rtTex, RAD_TEXTURE_2D_MULTISAMPLE, 1, RAD_RGBA8, offscreenWidth, offscreenHeight, 1, 4); | |
| RADtexture depthTex = radCreateTexture(device); | |
| radTextureAccess(depthTex, RAD_RENDER_TARGET_ACCESS_BIT); | |
| radTextureStorage(depthTex, RAD_TEXTURE_2D_MULTISAMPLE, 1, RAD_DEPTH24_STENCIL8, offscreenWidth, offscreenHeight, 1, 4); | |
| RADtexture tex1x = radCreateTexture(device); | |
| radTextureAccess(tex1x, RAD_RENDER_TARGET_ACCESS_BIT); | |
| radTextureStorage(tex1x, RAD_TEXTURE_2D, 1, RAD_RGBA8, offscreenWidth, offscreenHeight, 1, 0); | |
| // Create and bind a rendertarget handle | |
| RADrenderTargetHandle rtHandle = radGetTextureRenderTargetHandle(rtTex, RAD_TEXTURE_2D, RAD_RGBA8, 0, 0, 1); | |
| RADrenderTargetHandle depthHandle = radGetTextureRenderTargetHandle(depthTex, RAD_TEXTURE_2D, RAD_DEPTH24_STENCIL8, 0, 0, 1); | |
| radRasterSamples(raster, 4); | |
| radRtFormatColorSamples(rtFormat, 4); | |
| radRtFormatDepthStencilSamples(rtFormat, 4); | |
| RADpass pass = radCreatePass(device); | |
| radPassRenderTargets(pass, 1, &rtHandle, depthHandle, 0); | |
| radPassResolve(pass, RAD_RT_ATTACHMENT_COLOR0, tex1x); | |
| #else | |
| RADtexture rtTex = radCreateTexture(device); | |
| radTextureAccess(rtTex, RAD_RENDER_TARGET_ACCESS_BIT); | |
| radTextureStorage(rtTex, RAD_TEXTURE_2D, 1, RAD_RGBA8, offscreenWidth, offscreenHeight, 1, 0); | |
| RADtexture depthTex = radCreateTexture(device); | |
| radTextureAccess(depthTex, RAD_RENDER_TARGET_ACCESS_BIT); | |
| radTextureStorage(depthTex, RAD_TEXTURE_2D, 1, RAD_DEPTH24_STENCIL8, offscreenWidth, offscreenHeight, 1, 0); | |
| // Create and bind a rendertarget handle | |
| RADrenderTargetHandle rtHandle = radGetTextureRenderTargetHandle(rtTex, RAD_TEXTURE_2D, RAD_RGBA8, 0, 0, 1); | |
| RADrenderTargetHandle depthHandle = radGetTextureRenderTargetHandle(depthTex, RAD_TEXTURE_2D, RAD_DEPTH24_STENCIL8, 0, 0, 1); | |
| RADpass pass = radCreatePass(device); | |
| radPassRenderTargets(pass, 1, &rtHandle, depthHandle, 0); | |
| #endif | |
| radCompilePass(pass); | |
| radCompilePipeline(pipeline); | |
| RADpipelineHandle pipelineHandle = radGetPipelineHandle(pipeline); | |
| RADsampler sampler = radCreateSampler(device); | |
| // Commented out experiments to test different state settings. | |
| //radSamplerMinMagFilter(sampler, RAD_MIN_FILTER_NEAREST, RAD_MAG_FILTER_NEAREST); | |
| const int texWidth = 4, texHeight = 4; | |
| RADtexture texture = radCreateTexture(device); | |
| radTextureAccess(texture, RAD_TEXTURE_ACCESS_BIT); | |
| radTextureStorage(texture, RAD_TEXTURE_2D, 1, RAD_RGBA8, texWidth, texHeight, 1, 0); | |
| RADtextureHandle texHandle = radGetTextureSamplerHandle(texture, sampler, RAD_TEXTURE_2D, RAD_RGBA8, | |
| /*minLevel*/0, /*numLevels*/1, /*minLayer*/0, /*numLayers*/1); | |
| RADbuffer pbo = radCreateBuffer(device); | |
| radBufferAccess(pbo, RAD_COPY_READ_ACCESS_BIT); | |
| radBufferMapAccess(pbo, RAD_MAP_WRITE_BIT | RAD_MAP_PERSISTENT_BIT); | |
| radBufferStorage(pbo, texWidth*texHeight*4); | |
| unsigned char *texdata = (unsigned char *)radMapBuffer(pbo); | |
| // fill with texture data | |
| for (int j = 0; j < texWidth; ++j) { | |
| for (int i = 0; i < texHeight; ++i) { | |
| texdata[4*(j*texWidth+i)+0] = 0xFF*((i+j)&1); | |
| texdata[4*(j*texWidth+i)+1] = 0x00*((i+j)&1); | |
| texdata[4*(j*texWidth+i)+2] = ~(0xFF*((i+j)&1)); | |
| texdata[4*(j*texWidth+i)+3] = 0xFF; | |
| } | |
| } | |
| // XXX missing pixelpack object | |
| // Download the texture data | |
| radQueueCopyBufferToImage(queue, pbo, 0, texture, 0, 0, 0, 0, texWidth, texHeight, 1); | |
| radQueueBeginPass(queue, pass); | |
| // Some scissored clears | |
| { | |
| radQueueScissor(queue, 0, 0, offscreenWidth, offscreenHeight); | |
| float clearColor[4] = {0, 0, 0, 1}; | |
| radQueueClearColor(queue, 0, clearColor); | |
| radQueueClearDepth(queue, 1.0f); | |
| } | |
| { | |
| radQueueScissor(queue, 0, 0, offscreenWidth/2, offscreenHeight/2); | |
| float clearColor[4] = {0, 1, 0, 1}; | |
| radQueueClearColor(queue, 0, clearColor); | |
| } | |
| { | |
| radQueueScissor(queue, offscreenWidth/2, offscreenHeight/2, offscreenWidth/2, offscreenHeight/2); | |
| float clearColor[4] = {0, 0, 1, 1}; | |
| radQueueClearColor(queue, 0, clearColor); | |
| } | |
| radQueueScissor(queue, 0, 0, offscreenWidth, offscreenHeight); | |
| radQueueViewport(queue, 0, 0, offscreenWidth, offscreenHeight); | |
| RADbindGroupElement b[4] = {{vboHandle, 0, 0}, {vboHandle, sizeof(vertexData), 0}, {uboHandle, 0, 4*sizeof(float)}, {texHandle, 0, 0}}; | |
| RADbuffer bindGroup = AllocAndFillBuffer(device, b, sizeof(b), RAD_BINDGROUP_ACCESS_BIT, false); | |
| RADbindGroupHandle bindGroupHandle = radGetBindGroupHandle(bindGroup); | |
| if (useCopyQueue) { | |
| // Sync from copy queue to graphics queue. Note that we currently don't sync in the | |
| // opposite direction at the end of the frame, because radQueuePresent effectively | |
| // does a Finish so it isn't needed. | |
| RADsync sync = radCreateSync(device); | |
| radQueueFenceSync(copyQueue, sync, RAD_SYNC_ALL_GPU_COMMANDS_COMPLETE, 0); | |
| radQueueWaitSync(queue, sync); | |
| radReleaseSync(sync); | |
| } | |
| clock_t startTime = clock(); | |
| unsigned int numIterations = benchmark ? 1000000 : 1; | |
| switch (drawMode) { | |
| case QUEUE: | |
| for (unsigned int i = 0; i < numIterations; ++i) { | |
| // Bind the pipeline, bind vertex arrays and textures, and draw | |
| radQueueBindPipeline(queue, RAD_PIPELINE_TYPE_GRAPHICS, pipelineHandle); | |
| radQueueBindGroup(queue, RAD_VERTEX_SHADER_BIT | RAD_FRAGMENT_SHADER_BIT, 0, 4, bindGroupHandle, 0); | |
| //radQueueDrawArrays(queue, RAD_TRIANGLES, 0, 6); | |
| radQueueDrawElements(queue, RAD_TRIANGLES, RAD_INDEX_UNSIGNED_SHORT, 6, iboHandle, 0); | |
| } | |
| break; | |
| case TOKEN: | |
| { | |
| typedef struct Draw { | |
| RADuint pipelineHeader; | |
| RADtokenBindGraphicsPipeline pipeline; | |
| RADuint bindGroupHeader; | |
| RADtokenBindGroup bindGroup; | |
| RADuint drawElementsHeader; | |
| RADtokenDrawElements drawElements; | |
| } Draw; | |
| Draw d; | |
| memset(&d, 0, sizeof(d)); | |
| d.pipelineHeader = radGetTokenHeader(device, RAD_TOKEN_BIND_GRAPHICS_PIPELINE); | |
| d.pipeline.pipelineHandle = pipelineHandle; | |
| d.bindGroupHeader = radGetTokenHeader(device, RAD_TOKEN_BIND_GROUP); | |
| d.bindGroup.stages = RAD_VERTEX_SHADER_BIT | RAD_FRAGMENT_SHADER_BIT; | |
| d.bindGroup.group = 0; | |
| d.bindGroup.groupHandle = bindGroupHandle; | |
| d.bindGroup.offset = 0; | |
| d.bindGroup.count = 4; | |
| d.drawElementsHeader = radGetTokenHeader(device, RAD_TOKEN_DRAW_ELEMENTS); | |
| d.drawElements.indexHandle = iboHandle; | |
| d.drawElements.mode = RAD_TRIANGLES; | |
| d.drawElements.type = RAD_INDEX_UNSIGNED_SHORT; | |
| d.drawElements.count = 6; | |
| for (unsigned int i = 0; i < numIterations; ++i) { | |
| radQueueSubmitDynamic(queue, &d, sizeof(d)); | |
| } | |
| } | |
| break; | |
| case COMMAND: | |
| { | |
| RADcommandBuffer cmd = radCreateCommandBuffer(device, RAD_QUEUE_TYPE_GRAPHICS_AND_COMPUTE); | |
| radCmdBindPipeline(cmd, RAD_PIPELINE_TYPE_GRAPHICS, pipelineHandle); | |
| radCmdBindGroup(cmd, RAD_VERTEX_SHADER_BIT | RAD_FRAGMENT_SHADER_BIT, 0, 4, bindGroupHandle, 0); | |
| radCmdDrawElements(cmd, RAD_TRIANGLES, RAD_INDEX_UNSIGNED_SHORT, 6, iboHandle, 0); | |
| radCompileCommandBuffer(cmd); | |
| RADcommandHandle cmdHandle = radGetCommandHandle(cmd); | |
| for (unsigned int i = 0; i < numIterations; ++i) { | |
| radQueueSubmitCommands(queue, 1, &cmdHandle); | |
| } | |
| radReleaseCommandBuffer(cmd); | |
| } | |
| break; | |
| } | |
| if (benchmark) { | |
| clock_t currentTime = clock(); | |
| printf("%f\n", 1.0f*numIterations*CLOCKS_PER_SEC/(currentTime - startTime)); | |
| } | |
| radQueueEndPass(queue, pass); | |
| // Kickoff submitted command buffers for the queue | |
| //radFlushQueue(queue); | |
| #if USE_MULTISAMPLE | |
| radQueuePresent(queue, tex1x); | |
| #else | |
| radQueuePresent(queue, rtTex); | |
| #endif | |
| radReleaseProgram(pgm); | |
| radReleaseColorState(color); | |
| radReleaseDepthStencilState(depth); | |
| radReleaseVertexState(vertex); | |
| radReleaseRasterState(raster); | |
| radReleaseRtFormatState(rtFormat); | |
| radReleaseBuffer(vbo, RAD_TAG_AUTO); | |
| radReleaseBuffer(ibo, RAD_TAG_AUTO); | |
| radReleaseBuffer(pbo, RAD_TAG_AUTO); | |
| radReleaseBuffer(ubo, RAD_TAG_AUTO); | |
| radReleaseBuffer(bindGroup, RAD_TAG_AUTO); | |
| radReleaseTexture(texture, RAD_TAG_AUTO); | |
| radReleaseTexture(rtTex, RAD_TAG_AUTO); | |
| radReleaseTexture(depthTex, RAD_TAG_AUTO); | |
| radReleaseSampler(sampler); | |
| radReleasePipeline(pipeline); | |
| radReleasePass(pass); | |
| #if USE_MULTISAMPLE | |
| radReleaseTexture(tex1x, RAD_TAG_AUTO); | |
| #endif | |
| } | |