#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 | |
} | |