diff --git a/examples/basic.cpp b/examples/basic.cpp index 1cd18c5..0521862 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,8 +1,21 @@ #include +namespace +{ + +void update(sprstk* instance, float dt, void* userdata) +{ + for (int i = 0; i < 10; i += 2) + { + sprstk_put(instance, i, 0); + } +} + +} + int main() { - sprstk* instance = sprstk_new({.update = [](sprstk*, float, void*) {}}, nullptr); + sprstk* instance = sprstk_new({.update = update}, nullptr); sprstk_run(instance); diff --git a/include/sprstk/sprstk.h b/include/sprstk/sprstk.h index 2ecc2f5..0675254 100644 --- a/include/sprstk/sprstk.h +++ b/include/sprstk/sprstk.h @@ -24,6 +24,9 @@ void sprstk_del(sprstk* instance); void sprstk_run(sprstk* instance); void sprstk_stop(sprstk* instance); +void sprstk_put(sprstk* instance, unsigned int x, unsigned int y); +void sprstk_putz(sprstk* instance, unsigned int x, unsigned int y, unsigned int z_offset); + #ifdef __cplusplus } #endif diff --git a/src/sprstk.cpp b/src/sprstk.cpp index 040b19c..0cb3b48 100644 --- a/src/sprstk.cpp +++ b/src/sprstk.cpp @@ -10,32 +10,60 @@ const char* MESH_SHADER_CODE = R"( #extension GL_NV_mesh_shader : require -layout(local_size_x = 1) in; -layout(triangles, max_vertices = 3, max_primitives = 1) out; +layout (local_size_x = 32) in; +layout (triangles, max_vertices = 128, max_primitives = 64) out; layout (location = 0) out PerVertexData { vec4 color; } v_out[]; -const vec3 vertices[3] = {vec3(-1, -1, 0), vec3(0, 1, 0), vec3(1, -1, 0)}; -const vec3 colors[3] = {vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1)}; +layout (location = 1) uniform vec3 screen_size_and_pixel_scale; + +struct TileInfo +{ + uint position; +}; + +layout (std430, binding = 0) restrict readonly buffer TileInfos +{ + TileInfo[] tile_infos; +}; + +const uint indices[6] = { 0, 1, 2, 2, 1, 3 }; void main() { - gl_MeshVerticesNV[0].gl_Position = vec4(vertices[0], 1); - gl_MeshVerticesNV[1].gl_Position = vec4(vertices[1], 1); - gl_MeshVerticesNV[2].gl_Position = vec4(vertices[2], 1); + TileInfo info = tile_infos[gl_WorkGroupID.x]; + uint position_x = bitfieldExtract(info.position, 0, 13); + uint position_y = bitfieldExtract(info.position, 13, 13); + vec2 stack_position = vec2(position_x, position_y); - v_out[0].color = vec4(colors[0], 1); - v_out[1].color = vec4(colors[1], 1); - v_out[2].color = vec4(colors[2], 1); + vec2 positions[4] = { vec2(0, 0), vec2(1, 0), vec2(0, 1), vec2(1, 1) }; + for (uint i = 0; i < 4; i++) + { + positions[i] += stack_position; + positions[i] *= screen_size_and_pixel_scale.zz; + positions[i] /= screen_size_and_pixel_scale.xy; + } - gl_PrimitiveIndicesNV[0] = 0; - gl_PrimitiveIndicesNV[1] = 1; - gl_PrimitiveIndicesNV[2] = 2; + uint z_offset = bitfieldExtract(info.position, 26, 6); - gl_PrimitiveCountNV = 1; + for (uint i = 4 * gl_LocalInvocationID.x; i < 4 * gl_LocalInvocationID.x + 4; i++) + { + vec4 position = vec4(positions[i % 4], float(4 * gl_LocalInvocationID.x + z_offset) / 128, 1); + position.y += 8 * position.z / screen_size_and_pixel_scale.z; + gl_MeshVerticesNV[i].gl_Position = position; + + v_out[i].color = vec4(1, 1, 1, 0.1); + } + + for (uint i = 6 * gl_LocalInvocationID.x; i < 6 * gl_LocalInvocationID.x + 6; i++) + { + gl_PrimitiveIndicesNV[i] = 4 * gl_LocalInvocationID.x + indices[i % 6]; + } + + gl_PrimitiveCountNV = 64; } )"; @@ -55,6 +83,11 @@ void main() } )"; +struct TileInfo +{ + uint32_t position; +}; + class application_error : public std::runtime_error { public: @@ -105,15 +138,19 @@ public: int width, height; SDL_GetWindowSizeInPixels(sdl.window, &width, &height); glViewport(0, 0, width, height); + glProgramUniform3f(gl.program, 1, width, height, 32); } } uint64_t current_ticks = SDL_GetTicks(); float dt = (prev_ticks - current_ticks) / 1000.0f; + gl.tile_count = 0; callbacks.update(this, dt, userdata); - glDrawMeshTasksNV(0, 1); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + glDrawMeshTasksNV(0, gl.tile_count); SDL_GL_SwapWindow(sdl.window); } @@ -126,6 +163,16 @@ public: should_stop = true; } + void put(unsigned int x, unsigned int y, unsigned int z_offset = 0) + { + x &= 0b0001111111111111; + y &= 0b0001111111111111; + y <<= 13; + z_offset &= 0b111111; + z_offset <<= 26; + gl.tile_buffer_map[gl.tile_count++] = { x | y | z_offset }; + } + private: sprstk_callbacks callbacks; void* userdata; @@ -141,6 +188,9 @@ private: struct { unsigned int program; + unsigned int tile_buffer; + TileInfo* tile_buffer_map; + unsigned int tile_count; } gl; void init_sdl() @@ -155,6 +205,7 @@ private: SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); sdl.window = SDL_CreateWindow("sprstk", 640, 480, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); if (!sdl.window) @@ -186,6 +237,14 @@ private: throw application_error("Mesh shaders not supported"); } + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + + glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userdata) + { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "%s", message); + }, nullptr); + int success; char info_log[512]; @@ -229,10 +288,24 @@ private: } glUseProgram(gl.program); + + int width, height; + SDL_GetWindowSizeInPixels(sdl.window, &width, &height); + glViewport(0, 0, width, height); + glProgramUniform3f(gl.program, 1, width, height, 32); + + glGenBuffers(1, &gl.tile_buffer); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, gl.tile_buffer); + glNamedBufferStorage(gl.tile_buffer, sizeof(TileInfo) * (1 << 26), nullptr, GL_MAP_WRITE_BIT | GL_MAP_COHERENT_BIT | GL_MAP_PERSISTENT_BIT); + + gl.tile_count = 0; + gl.tile_buffer_map = (TileInfo*)glMapNamedBufferRange(gl.tile_buffer, 0, sizeof(TileInfo) * (1 << 26), GL_MAP_WRITE_BIT | GL_MAP_COHERENT_BIT); } void destroy_gl() { + glDeleteProgram(gl.program); + glDeleteBuffers(1, &gl.tile_buffer); SDL_GL_DestroyContext(sdl.context); } }; @@ -281,4 +354,14 @@ void sprstk_stop(sprstk* instance) instance->stop(); } +void sprstk_put(sprstk* instance, unsigned int x, unsigned int y) +{ + instance->put(x, y); +} + +void sprstk_putz(sprstk* instance, unsigned int x, unsigned int y, unsigned int z_offset) +{ + instance->put(x, y, z_offset); +} + }