scary quads and nice sprites
This commit is contained in:
parent
e3d87525d4
commit
6e65700f38
@ -1,18 +0,0 @@
|
||||
#version 330 core
|
||||
|
||||
// viewsize.x = viewport width in pixels; viewsize.y = viewport height in pixels
|
||||
uniform ivec2 viewsize;
|
||||
uniform ivec2 texturesize;
|
||||
|
||||
// texture uv coordinate in texture space
|
||||
in vec2 uv;
|
||||
uniform sampler2DRect ts;
|
||||
|
||||
const vec3 textcolor = vec3(1.0, 1.0, 1.0);
|
||||
|
||||
|
||||
void main()
|
||||
{
|
||||
//gl_FragColor = vec4(1.0f,0.0f,0.0f,1.0f);
|
||||
gl_FragColor = vec4(textcolor, texture(ts, uv));
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#version 330 core
|
||||
|
||||
// viewsize.x = viewport width in pixels; viewsize.y = viewport height in pixels
|
||||
uniform ivec2 viewsize;
|
||||
uniform ivec2 texturesize;
|
||||
|
||||
// both position and and uv are in pixels, they where converted to floats when
|
||||
// passed to the shader
|
||||
layout(location = 0) in vec2 position;
|
||||
layout(location = 1) in vec2 txcoord;
|
||||
|
||||
out vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 v = vec2(float(viewsize.x), float(viewsize.y));
|
||||
|
||||
// vec2 p = vec2(position.x*2.0f/v.x - 1.0f, position.y*2.0f/v.y - 1.0f);
|
||||
vec2 p = vec2(position.x*2.0/v.x - 1.0, 1.0 - position.y*2.0/v.y);
|
||||
vec4 pos = vec4(p.x, p.y, 0.0f, 1.0f);
|
||||
|
||||
gl_Position = pos;
|
||||
// since the texture is a GL_TEXTURE_RECTANGLE the coordintes do not need to
|
||||
// be normalized
|
||||
uv = vec2(txcoord.x, txcoord.y);
|
||||
}
|
14
resources/shaders/source/sprite.frag.glsl
Normal file
14
resources/shaders/source/sprite.frag.glsl
Normal file
@ -0,0 +1,14 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
|
||||
layout(set = 2, binding = 0) uniform sampler2D tx;
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 ts = textureSize(tx, 0);
|
||||
vec2 fts = vec2(float(ts.x), float(ts.y));
|
||||
vec2 real_uv = uv / fts;
|
||||
fragColor = texture(tx, real_uv);
|
||||
}
|
22
resources/shaders/source/sprite.vert.glsl
Normal file
22
resources/shaders/source/sprite.vert.glsl
Normal file
@ -0,0 +1,22 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 1, binding = 0) uniform Viewport {
|
||||
ivec2 view;
|
||||
ivec2 not_needed;
|
||||
};
|
||||
|
||||
layout(location = 0) in ivec2 position;
|
||||
layout(location = 1) in ivec2 in_uv;
|
||||
layout(location = 2) in ivec4 color;
|
||||
|
||||
layout(location = 0) out vec2 out_uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pos;
|
||||
pos.x = float(position.x)*2.0 / view.x - 1.0;
|
||||
pos.y = -(float(position.y)*2.0 / view.y - 1.0);
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
|
||||
out_uv = vec2(float(in_uv.x), float(in_uv.y));
|
||||
}
|
119
src/renderer.c3
119
src/renderer.c3
@ -29,12 +29,13 @@ struct Texture {
|
||||
}
|
||||
|
||||
// The GPU buffers that contain quad info, the size is determined by MAX_QUAD_BATCH
|
||||
const int MAX_QUAD_BATCH = 16;
|
||||
const int MAX_QUAD_BATCH = 128;
|
||||
struct QuadBuffer {
|
||||
sdl::GPUBuffer* vert_buf;
|
||||
sdl::GPUBuffer* idx_buf;
|
||||
bool initialized;
|
||||
int count;
|
||||
int off; // the offset to draw from
|
||||
}
|
||||
|
||||
alias ShaderList = List{Shader};
|
||||
@ -373,14 +374,15 @@ fn void Renderer.create_pipeline(&self, String shader_name, PipelineType type)
|
||||
self.pipelines.push(p);
|
||||
}
|
||||
|
||||
// NOTE: with TEXTUREUSAGE_SAMPLER the texture format cannot be intger _UINT so it has to be nermalized
|
||||
enum TextureType : (GPUTextureFormat format) {
|
||||
FULL_COLOR = GPU_TEXTUREFORMAT_R8G8B8A8_UINT,
|
||||
JUST_ALPHA = GPU_TEXTUREFORMAT_R8_UINT
|
||||
FULL_COLOR = GPU_TEXTUREFORMAT_R8G8B8A8_UNORM,
|
||||
JUST_ALPHA = GPU_TEXTUREFORMAT_R8_UNORM
|
||||
}
|
||||
|
||||
// create a new gpu texture from a pixel buffer, the format has to be specified
|
||||
// the new texture s given an id and pushed into a texture list
|
||||
fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels, ushort width, ushort height)
|
||||
fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels, uint width, uint height)
|
||||
{
|
||||
uint id = name.hash();
|
||||
|
||||
@ -393,7 +395,7 @@ fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels
|
||||
.width = width,
|
||||
.height = height,
|
||||
.layer_count_or_depth = 1,
|
||||
.num_levels = 0, // no mip maps
|
||||
.num_levels = 1, // no mip maps so just one level
|
||||
// .sample_count not used since the texture is not a render target
|
||||
};
|
||||
|
||||
@ -429,7 +431,7 @@ fn void Renderer.new_texture(&self, String name, TextureType type, char[] pixels
|
||||
self.update_texture(name, pixels, width, height);
|
||||
}
|
||||
|
||||
fn void Renderer.update_texture(&self, String name, char[] pixels, ushort width, ushort height, ushort x = 0, ushort y = 0)
|
||||
fn void Renderer.update_texture(&self, String name, char[] pixels, uint width, uint height, uint x = 0, uint y = 0)
|
||||
{
|
||||
Texture* t = self.textures.get_from_name(name);
|
||||
if (t == null || t.texture == null) {
|
||||
@ -482,6 +484,87 @@ fn void Renderer.update_texture(&self, String name, char[] pixels, ushort width,
|
||||
}
|
||||
|
||||
|
||||
fn bool Renderer.push_sprite(&self, short x, short y, short w, short h, short u, short v) {
|
||||
if (self.quad_buffer.count >= MAX_QUAD_BATCH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// upload the quad data to the gpu
|
||||
if (self.quad_buffer.initialized == false) {
|
||||
unreachable("quad buffer not initialized");
|
||||
}
|
||||
|
||||
GPUTransferBuffer* buf = sdl::create_gpu_transfer_buffer(self.gpu,
|
||||
&&(GPUTransferBufferCreateInfo){.usage = GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = Quad.sizeof}
|
||||
);
|
||||
if (buf == null) {
|
||||
unreachable("failed to create gpu transfer buffer: %s", sdl::get_error());
|
||||
}
|
||||
|
||||
Quad* quad = (Quad*)sdl::map_gpu_transfer_buffer(self.gpu, buf, false);
|
||||
if (quad == null) {
|
||||
unreachable("failed to map gpu transfer buffer: %s", sdl::get_error());
|
||||
}
|
||||
|
||||
/* v1 v4
|
||||
* +-------------+
|
||||
* | _/|
|
||||
* | _/ |
|
||||
* | 1 _/ |
|
||||
* | _/ |
|
||||
* | _/ |
|
||||
* | _/ 2 |
|
||||
* |/ |
|
||||
* +-------------+
|
||||
* v2 v3
|
||||
*/
|
||||
quad.vertices.v1 = {.pos = {.x = x, .y = y}, .uv = {.u = u, .v = v}};
|
||||
quad.vertices.v2 = {.pos = {.x = x, .y = y+h}, .uv = {.u = u, .v = v+h}};
|
||||
quad.vertices.v3 = {.pos = {.x = x+w, .y = y+h}, .uv = {.u = u+w, .v = v+h}};
|
||||
quad.vertices.v4 = {.pos = {.x = x+w, .y = y}, .uv = {.u = u+w, .v = v}};
|
||||
// triangle 1 indices
|
||||
quad.indices.i1 = 0; // v1
|
||||
quad.indices.i2 = 1; // v2
|
||||
quad.indices.i3 = 3; // v4
|
||||
// triangle 2 indices
|
||||
quad.indices.i4 = 1; // v2
|
||||
quad.indices.i5 = 2; // v3
|
||||
quad.indices.i6 = 3; // v4
|
||||
|
||||
sdl::unmap_gpu_transfer_buffer(self.gpu, buf);
|
||||
|
||||
GPUCommandBuffer* cmd = sdl::acquire_gpu_command_buffer(self.gpu);
|
||||
if (cmd == null) {
|
||||
unreachable("failed to upload quad at acquiring command buffer: %s", sdl::get_error());
|
||||
}
|
||||
GPUCopyPass* cpy = sdl::begin_gpu_copy_pass(cmd);
|
||||
|
||||
// upload vertices
|
||||
QuadBuffer* qb = &self.quad_buffer;
|
||||
sdl::upload_to_gpu_buffer(cpy,
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.vertices.offsetof},
|
||||
&&(GPUBufferRegion){.buffer = qb.vert_buf, .offset = qb.count * Quad.vertices.sizeof, .size = Quad.vertices.sizeof},
|
||||
false
|
||||
);
|
||||
// upload indices
|
||||
sdl::upload_to_gpu_buffer(cpy,
|
||||
&&(GPUTransferBufferLocation){.transfer_buffer = buf, .offset = Quad.indices.offsetof},
|
||||
&&(GPUBufferRegion){.buffer = qb.idx_buf, .offset = qb.count * Quad.indices.sizeof, .size = Quad.indices.sizeof},
|
||||
false
|
||||
);
|
||||
|
||||
sdl::end_gpu_copy_pass(cpy);
|
||||
if (!sdl::submit_gpu_command_buffer(cmd)) {
|
||||
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
||||
}
|
||||
sdl::release_gpu_transfer_buffer(self.gpu, buf);
|
||||
//sdl::wait_for_gpu_idle(self.gpu);
|
||||
|
||||
qb.count++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Push a quad into the quad buffer, return true on success and false on failure
|
||||
fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color)
|
||||
{
|
||||
@ -558,7 +641,7 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
||||
unreachable("failed to upload quads at submit command buffer: %s", sdl::get_error());
|
||||
}
|
||||
sdl::release_gpu_transfer_buffer(self.gpu, buf);
|
||||
sdl::wait_for_gpu_idle(self.gpu);
|
||||
//sdl::wait_for_gpu_idle(self.gpu);
|
||||
|
||||
qb.count++;
|
||||
|
||||
@ -570,17 +653,27 @@ fn bool Renderer.push_quad(&self, short x, short y, short w, short h, uint color
|
||||
fn void Renderer.draw_quads(&self, GPURenderPass* pass)
|
||||
{
|
||||
QuadBuffer* qb = &self.quad_buffer;
|
||||
sdl::bind_gpu_vertex_buffers(pass, 0, (GPUBufferBinding[]){{.buffer = qb.vert_buf, .offset = 0}}, 1);
|
||||
sdl::bind_gpu_index_buffer(pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = 0}, GPU_INDEXELEMENTSIZE_16BIT);
|
||||
|
||||
if (qb.off == qb.count) return;
|
||||
|
||||
sdl::bind_gpu_vertex_buffers(pass, 0, (GPUBufferBinding[]){{.buffer = qb.vert_buf, .offset = qb.off*Quad.vertices.sizeof}}, 1);
|
||||
sdl::bind_gpu_index_buffer(pass, &&(GPUBufferBinding){.buffer = qb.idx_buf, .offset = qb.off*Quad.indices.sizeof}, GPU_INDEXELEMENTSIZE_16BIT);
|
||||
|
||||
// we need instancing to not do this
|
||||
for (int i = 0; i < qb.count; i++) {
|
||||
for (int i = 0; i < qb.count - qb.off; i++) {
|
||||
sdl::draw_gpu_indexed_primitives(pass, 6, 1, i*6, i*4, 0);
|
||||
}
|
||||
|
||||
qb.count = 0;
|
||||
qb.off = qb.count;
|
||||
}
|
||||
|
||||
fn void Renderer.reset_quads(&self)
|
||||
{
|
||||
self.quad_buffer.count = 0;
|
||||
self.quad_buffer.off = 0;
|
||||
}
|
||||
|
||||
|
||||
// TODO: fn Renderer.draw_sprite, same as draw_quad but also bind the texture
|
||||
// TODO: fn Renderer.begin_render
|
||||
// TODO: fn Renderer.end_render
|
||||
@ -599,4 +692,8 @@ fn void Renderer.draw_quads(&self, GPURenderPass* pass)
|
||||
* - draw
|
||||
* - push uniform
|
||||
* - draw
|
||||
*
|
||||
* 2. The GPU buffers are read per-command-buffer and not per
|
||||
* render pass. So I cannot override an element in the buffer
|
||||
* before submitting the command buffer.
|
||||
*/
|
@ -2,6 +2,8 @@ import sdlrenderer::ren;
|
||||
import std::io;
|
||||
import std::thread;
|
||||
import sdl3::sdl;
|
||||
import std::compression::qoi;
|
||||
import std::core::mem::allocator;
|
||||
|
||||
struct Viewsize @align(16) {
|
||||
int w, h;
|
||||
@ -13,18 +15,31 @@ fn int main()
|
||||
ren::Renderer ren;
|
||||
ren.init("test window");
|
||||
|
||||
// TODO: these could be the same function
|
||||
ren.load_spirv_shader_from_file("rect shader", "resources/shaders/compiled/rect.vert.spv", "resources/shaders/compiled/rect.frag.spv", 0, 1);
|
||||
ren.create_pipeline("rect shader", RECT);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
|
||||
// load the tux qoi image
|
||||
QOIDesc img_desc;
|
||||
char[] img_pixels = qoi::read(allocator::temp(), "resources/tux.qoi", &img_desc)!!;
|
||||
// and put it in a texture
|
||||
ren.new_texture("tux", FULL_COLOR, img_pixels, img_desc.width, img_desc.height);
|
||||
// create a new pipeline to use the texture
|
||||
ren.load_spirv_shader_from_file("sprite shader", "resources/shaders/compiled/sprite.vert.spv", "resources/shaders/compiled/sprite.frag.spv", 1, 1);
|
||||
ren.create_pipeline("sprite shader", SPRITE);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
GPUCommandBuffer* cmdbuf = sdl::acquire_gpu_command_buffer(ren.gpu);
|
||||
GPUTexture* swapchain_texture;
|
||||
sdl::wait_and_acquire_gpu_swapchain_texture(cmdbuf, ren.win, &swapchain_texture, null, null);
|
||||
|
||||
GPURenderPass* pass;
|
||||
GPUGraphicsPipeline* p;
|
||||
Viewsize v = {.w = 640, .h = 480};
|
||||
|
||||
// Colored Rectangles Render Pass
|
||||
// FIXME: if doing damage tracking DO NOT clear the screen
|
||||
GPURenderPass* pass = sdl::begin_gpu_render_pass(cmdbuf,
|
||||
pass = sdl::begin_gpu_render_pass(cmdbuf,
|
||||
&&(GPUColorTargetInfo){
|
||||
.texture = swapchain_texture,
|
||||
.mip_level = 0,
|
||||
@ -53,24 +68,74 @@ fn int main()
|
||||
// rect 3
|
||||
ren.push_quad(200,300,50,50,0xffff0000);
|
||||
|
||||
GPUGraphicsPipeline* p = ren.pipelines.get_from_name("rect shader").pipeline;
|
||||
p = ren.pipelines.get_from_name("rect shader").pipeline;
|
||||
if (p == null) {
|
||||
unreachable("no pipeline");
|
||||
}
|
||||
|
||||
sdl::bind_gpu_graphics_pipeline(pass, p);
|
||||
|
||||
Viewsize v = {.w = 640, .h = 480};
|
||||
v.ox = 50*i;
|
||||
v.oy = 50*i;
|
||||
|
||||
v.ox = 10*i;
|
||||
v.oy = 10*i;
|
||||
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &v, Viewsize.sizeof);
|
||||
|
||||
ren.draw_quads(pass);
|
||||
|
||||
sdl::end_gpu_render_pass(pass);
|
||||
// End Rectangle Render Pass
|
||||
|
||||
|
||||
// Textured Rectangles Render Pass
|
||||
pass = sdl::begin_gpu_render_pass(cmdbuf,
|
||||
&&(GPUColorTargetInfo){
|
||||
.texture = swapchain_texture,
|
||||
.mip_level = 0,
|
||||
.layer_or_depth_plane = 0,
|
||||
.clear_color = {.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0},
|
||||
.load_op = GPU_LOADOP_DONT_CARE, // clear the screen at the start of the render pass
|
||||
.store_op = GPU_STOREOP_STORE,
|
||||
.resolve_texture = null,
|
||||
.resolve_mip_level = 0,
|
||||
.resolve_layer = 0,
|
||||
.cycle = false,
|
||||
.cycle_resolve_texture = false
|
||||
},
|
||||
1,
|
||||
null // huh
|
||||
);
|
||||
|
||||
if (pass == null) {
|
||||
unreachable("render pass creation went wrong: %s", sdl::get_error());
|
||||
}
|
||||
|
||||
p = ren.pipelines.get_from_name("sprite shader").pipeline;
|
||||
if (p == null) {
|
||||
unreachable("no pipeline");
|
||||
}
|
||||
|
||||
sdl::bind_gpu_graphics_pipeline(pass, p);
|
||||
// in this case it is not an offset but the texture size in pixels
|
||||
v.ox = 54;
|
||||
v.oy = 64;
|
||||
sdl::push_gpu_vertex_uniform_data(cmdbuf, 1, &v, Viewsize.sizeof);
|
||||
|
||||
// bind the pipeline's sampler
|
||||
ren::Texture* tx = ren.textures.get_from_name("tux");
|
||||
sdl::bind_gpu_fragment_samplers(pass, 0,
|
||||
(GPUTextureSamplerBinding[]){{.texture = tx.texture, .sampler = tx.sampler}}, 1
|
||||
);
|
||||
|
||||
// tux
|
||||
ren.push_sprite(300, 0, 54, 64, 0, 0);
|
||||
|
||||
ren.draw_quads(pass);
|
||||
|
||||
sdl::end_gpu_render_pass(pass);
|
||||
// End Textured Rectangle Render Pass
|
||||
|
||||
sdl::submit_gpu_command_buffer(cmdbuf);
|
||||
|
||||
ren.reset_quads();
|
||||
thread::sleep_ms(250);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user