소스 검색

Render texture support in GL

Karl Zylinski 1 개월 전
부모
커밋
100d2362e2
5개의 변경된 파일138개의 추가작업 그리고 29개의 파일을 삭제
  1. 8 6
      examples/render_texture/render_texture.odin
  2. 20 6
      karl2d.odin
  3. 25 7
      render_backend_d3d11.odin
  4. 84 10
      render_backend_gl.odin
  5. 1 0
      render_backend_interface.odin

+ 8 - 6
examples/render_texture/render_texture.odin

@@ -27,13 +27,12 @@ main :: proc() {
 	}
 
 	k2.init(1080, 1080, "Karl2D Render Texture Example")
-	k2.set_window_position(300, 100)
-
-	render_texture := k2.create_render_texture(128, 128)
+	render_texture := k2.create_render_texture(75, 48)
 
 	for !k2.shutdown_wanted() {
 		k2.process_events()
 
+
 		k2.set_render_texture(render_texture)
 		k2.clear(k2.BLUE)
 
@@ -45,10 +44,13 @@ main :: proc() {
 		
 		k2.set_render_texture(nil)
 
-		k2.clear(k2.GRAY)
-		k2.draw_texture_ex(render_texture.texture, {0, 0, 128, 128}, {0, 0, 1080, 1080}, {}, 0, k2.WHITE)
+		k2.clear(k2.BLACK)
+
+		rt_size := k2.get_texture_rect(render_texture.texture)
+
+		k2.draw_texture_ex(render_texture.texture, rt_size, {0, 0, rt_size.w * 5, rt_size.h * 5}, {}, 0, k2.WHITE)
 		k2.draw_texture(render_texture.texture, {400, 20}, k2.WHITE)
-		k2.draw_texture_ex(render_texture.texture, {0, 0, 128, 128}, {512, 512, 512, 512}, {}, 70, k2.WHITE)
+		k2.draw_texture_ex(render_texture.texture, rt_size, {512, 512, rt_size.w * 5, rt_size.h * 5}, {}, 70, k2.WHITE)
 
 		k2.present()
 		free_all(context.temp_allocator)

+ 20 - 6
karl2d.odin

@@ -691,7 +691,16 @@ draw_texture_ex :: proc(tex: Texture, src: Rect, dst: Rect, origin: Vec2, rotati
 		uv2.x -= us.x
 		uv3.x += us.x
 		uv4.x -= us.x
-		uv5.x += us.x		
+		uv5.x += us.x
+	}
+
+	// HACK: We ask the render backend if this texture needs flipping. The idea is that GL will
+	// flip render textures, so we need to automatically unflip them.
+	//
+	// Could we do something with the projection matrix while drawing into those render textures
+	// instead? I tried that, but couldn't get it to work.
+	if rb.texture_needs_vertical_flip(tex.handle) {
+		flip_y = !flip_y
 	}
 
 	if flip_y {
@@ -724,13 +733,13 @@ draw_text :: proc(text: string, pos: Vec2, font_size: f32, color: Color) {
 	draw_text_ex(s.default_font, text, pos, font_size, color)
 }
 
-draw_text_ex :: proc(font: Font_Handle, text: string, pos: Vec2, font_size: f32, color: Color) {
-	if int(font) >= len(s.fonts) {
+draw_text_ex :: proc(font_handle: Font_Handle, text: string, pos: Vec2, font_size: f32, color: Color) {
+	if int(font_handle) >= len(s.fonts) {
 		return
 	}
 
-	set_font(font)
-	font := &s.fonts[font]
+	set_font(font_handle)
+	font := &s.fonts[font_handle]
 	fs.SetSize(&s.fs, font_size)
 	iter := fs.TextIterInit(&s.fs, pos.x, pos.y+font_size/2, text)
 
@@ -893,7 +902,7 @@ create_render_texture :: proc(width: int, height: int) -> Render_Texture {
 		texture = { 
 			handle = texture,
 			width = width,
-			height = height,	
+			height = height,
 		},
 		render_target = render_target,
 	}
@@ -915,6 +924,7 @@ set_render_texture :: proc(render_texture: Maybe(Render_Texture)) {
 
 		draw_current_batch()
 		s.batch_render_target = rt.render_target
+		s.proj_matrix = make_default_projection(rt.texture.width, rt.texture.height)
 	} else {
 		if s.batch_render_target == RENDER_TARGET_NONE {
 			return
@@ -922,6 +932,7 @@ set_render_texture :: proc(render_texture: Maybe(Render_Texture)) {
 
 		draw_current_batch()
 		s.batch_render_target = RENDER_TARGET_NONE
+		s.proj_matrix = make_default_projection(win.get_width(), win.get_height())
 	}
 }
 
@@ -1348,6 +1359,9 @@ Texture :: struct {
 	handle: Texture_Handle,
 	width: int,
 	height: int,
+
+	// Hack to flip OpenGL render textures
+	flip_on_draw: bool,
 }
 
 Load_Texture_Option :: enum {

+ 25 - 7
render_backend_d3d11.odin

@@ -21,6 +21,7 @@ RENDER_BACKEND_INTERFACE_D3D11 :: Render_Backend_Interface {
 	load_texture = d3d11_load_texture,
 	update_texture = d3d11_update_texture,
 	destroy_texture = d3d11_destroy_texture,
+	texture_needs_vertical_flip = d3d11_texture_needs_vertical_flip,
 	create_render_texture = d3d11_create_render_texture,
 	destroy_render_target = d3d11_destroy_render_target,
 	set_texture_filter = d3d11_set_texture_filter,
@@ -295,13 +296,6 @@ d3d11_draw :: proc(
 		}
 	}
 
-	viewport := d3d11.VIEWPORT{
-		0, 0,
-		f32(s.width), f32(s.height),
-		0, 1,
-	}
-
-	dc->RSSetViewports(1, &viewport)
 	dc->RSSetState(s.rasterizer_state)
 
 	scissor_rect := d3d11.RECT {
@@ -335,8 +329,24 @@ d3d11_draw :: proc(
 
 	if rt := hm.get(&s.render_targets, render_target); rt != nil {
 		dc->OMSetRenderTargets(1, &rt.render_target_view, rt.depth_stencil_texture_view)
+
+		viewport := d3d11.VIEWPORT{
+			0, 0,
+			f32(rt.width), f32(rt.height),
+			0, 1,
+		}
+
+		dc->RSSetViewports(1, &viewport)
 	} else {
 		dc->OMSetRenderTargets(1, &s.framebuffer_view, s.depth_buffer_view)
+
+		viewport := d3d11.VIEWPORT{
+			0, 0,
+			f32(s.width), f32(s.height),
+			0, 1,
+		}
+
+		dc->RSSetViewports(1, &viewport)
 	}
 
 	dc->OMSetDepthStencilState(s.depth_stencil_state, 0)
@@ -484,6 +494,8 @@ d3d11_create_render_texture :: proc(width: int, height: int) -> (Texture_Handle,
 		depth_stencil_texture = depth_stencil_texture,
 		depth_stencil_texture_view = depth_stencil_texture_view,
 		render_target_view = render_target_view,
+		width = width,
+		height = height,
 	}
 
 	return hm.add(&s.textures, d3d11_texture), hm.add(&s.render_targets, d3d11_render_target)
@@ -538,6 +550,10 @@ d3d11_destroy_texture :: proc(th: Texture_Handle) {
 	hm.remove(&s.textures, th)
 }
 
+d3d11_texture_needs_vertical_flip :: proc(th: Texture_Handle) -> bool {
+	return false
+}
+
 d3d11_set_texture_filter :: proc(
 	th: Texture_Handle,
 	scale_down_filter: Texture_Filter,
@@ -1024,6 +1040,8 @@ D3D11_Render_Target :: struct {
 	depth_stencil_texture: ^d3d11.ITexture2D,
 	depth_stencil_texture_view: ^d3d11.IDepthStencilView,
 	render_target_view: ^d3d11.IRenderTargetView,
+	width: int,
+	height: int,
 }
 
 dxgi_format_from_pixel_format :: proc(f: Pixel_Format) -> dxgi.FORMAT {

+ 84 - 10
render_backend_gl.odin

@@ -20,6 +20,7 @@ RENDER_BACKEND_INTERFACE_GL :: Render_Backend_Interface {
 	load_texture = gl_load_texture,
 	update_texture = gl_update_texture,
 	destroy_texture = gl_destroy_texture,
+	texture_needs_vertical_flip = gl_texture_needs_vertical_flip,
 	create_render_texture = gl_create_render_texture,
 	destroy_render_target = gl_destroy_render_target,
 	set_texture_filter = gl_set_texture_filter,
@@ -49,6 +50,7 @@ GL_State :: struct {
 	ctx: GL_Context,
 	vertex_buffer_gpu: u32,
 	textures: hm.Handle_Map(GL_Texture, Texture_Handle, 1024*10),
+	render_targets: hm.Handle_Map(GL_Render_Target, Render_Target_Handle, 128),
 }
 
 GL_Shader_Constant_Buffer :: struct {
@@ -82,12 +84,23 @@ GL_Texture :: struct {
 	handle: Texture_Handle,
 	id: u32,
 	format: Pixel_Format,
+
+	// Because render targets are up-side-down
+	needs_vertical_flip: bool,
 }
 
 GL_Texture_Binding :: struct {
 	loc: i32,
 }
 
+GL_Render_Target :: struct {
+	handle: Render_Target_Handle,
+	depth: u32,
+	framebuffer: u32,
+	width: int,
+	height: int,
+}
+
 GL_Shader :: struct {
 	handle: Shader_Handle,
 
@@ -137,7 +150,15 @@ gl_shutdown :: proc() {
 	_gl_destroy_context(s.ctx)
 }
 
-gl_clear :: proc(render_texture: Render_Target_Handle, color: Color) {
+gl_clear :: proc(render_target: Render_Target_Handle, color: Color) {
+	if rt := hm.get(&s.render_targets, render_target); rt != nil {
+		gl.BindFramebuffer(gl.FRAMEBUFFER, rt.framebuffer)
+		gl.Viewport(0, 0, i32(rt.width), i32(rt.height))
+	} else {
+		gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
+		gl.Viewport(0, 0, i32(s.width), i32(s.height))
+	}
+
 	c := f32_color_from_color(color)
 	gl.ClearColor(c.r, c.g, c.b, c.a)
 	gl.ClearDepth(-1)
@@ -150,7 +171,7 @@ gl_present :: proc() {
 
 gl_draw :: proc(
 	shd: Shader,
-	render_texture: Render_Target_Handle,
+	render_target: Render_Target_Handle,
 	bound_textures: []Texture_Handle,
 	scissor: Maybe(Rect),
 	blend_mode: Blend_Mode,
@@ -310,6 +331,16 @@ gl_draw :: proc(
 	}
 
 	gl.UnmapBuffer(gl.ARRAY_BUFFER)
+
+	if rt := hm.get(&s.render_targets, render_target); rt != nil {
+		gl.BindFramebuffer(gl.FRAMEBUFFER, rt.framebuffer)
+		gl.Viewport(0, 0, i32(rt.width), i32(rt.height))
+	} else {
+		gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
+		gl.Viewport(0, 0, i32(s.width), i32(s.height))
+	}
+	 // Render on the whole framebuffer, complete from the lower left corner to the upper right
+
 	gl.DrawArrays(gl.TRIANGLES, 0, i32(len(vertex_buffer)/shd.vertex_size))
 }
 
@@ -335,7 +366,7 @@ gl_set_internal_state :: proc(state: rawptr) {
 	s = (^GL_State)(state)
 }
 
-create_texture :: proc(width: int, height: int, format: Pixel_Format, data: rawptr) -> Texture_Handle {
+create_texture :: proc(width: int, height: int, format: Pixel_Format, data: rawptr) -> GL_Texture {
 	id: u32
 	gl.GenTextures(1, &id)
 	gl.BindTexture(gl.TEXTURE_2D, id)
@@ -348,20 +379,18 @@ create_texture :: proc(width: int, height: int, format: Pixel_Format, data: rawp
 	pf := gl_translate_pixel_format(format)
 	gl.TexImage2D(gl.TEXTURE_2D, 0, pf, i32(width), i32(height), 0, gl.RGBA, gl.UNSIGNED_BYTE, data)
 
-	tex := GL_Texture {
+	return {
 		id = id,
 		format = format,
 	}
-
-	return hm.add(&s.textures, tex)
 }
 
 gl_create_texture :: proc(width: int, height: int, format: Pixel_Format) -> Texture_Handle {
-	return create_texture(width, height, format, nil)
+	return hm.add(&s.textures, create_texture(width, height, format, nil))
 }
 
 gl_load_texture :: proc(data: []u8, width: int, height: int, format: Pixel_Format) -> Texture_Handle {
-	return create_texture(width, height, format, raw_data(data))
+	return hm.add(&s.textures, create_texture(width, height, format, raw_data(data)))
 }
 
 gl_update_texture :: proc(th: Texture_Handle, data: []u8, rect: Rect) -> bool {
@@ -387,12 +416,57 @@ gl_destroy_texture :: proc(th: Texture_Handle) {
 	hm.remove(&s.textures, th)
 }
 
+gl_texture_needs_vertical_flip :: proc(th: Texture_Handle) -> bool {
+	tex := hm.get(&s.textures, th)
+
+	if tex == nil {
+		return false
+	}
+
+	return tex.needs_vertical_flip
+}
+
 gl_create_render_texture :: proc(width: int, height: int) -> (Texture_Handle, Render_Target_Handle) {
-	return {}, {}
+	texture := create_texture(width, height, .RGBA_32_Float, nil)
+	texture.needs_vertical_flip = true
+	
+	framebuffer: u32
+	gl.GenFramebuffers(1, &framebuffer)
+	gl.BindFramebuffer(gl.FRAMEBUFFER, framebuffer)
+
+	depth: u32
+	gl.GenRenderbuffers(1, &depth)
+	gl.BindRenderbuffer(gl.RENDERBUFFER, depth)
+	gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT, i32(width), i32(height))
+	gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depth)
+
+	gl.FramebufferTexture(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture.id, 0)
+	draw_buffers := u32(gl.COLOR_ATTACHMENT0)
+	gl.DrawBuffers(1, &draw_buffers)
+
+	if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE {
+		log.errorf("Failed creating frame buffer of size %v x %v", width, height)
+		return {}, {}
+	}
+
+	gl.BindFramebuffer(gl.FRAMEBUFFER, 0)
+	gl.BindRenderbuffer(gl.RENDERBUFFER, 0)
+
+	rt := GL_Render_Target {
+		depth = depth,
+		framebuffer = framebuffer,
+		width = width,
+		height = height,
+	}
+
+	return hm.add(&s.textures, texture), hm.add(&s.render_targets, rt)
 }
 
 gl_destroy_render_target :: proc(render_target: Render_Target_Handle) {
-	
+	if rt := hm.get(&s.render_targets, render_target); rt != nil {
+		gl.DeleteRenderbuffers(1, &rt.depth)
+		gl.DeleteFramebuffers(1, &rt.framebuffer)
+	}
 }
 
 gl_set_texture_filter :: proc(

+ 1 - 0
render_backend_interface.odin

@@ -39,6 +39,7 @@ Render_Backend_Interface :: struct #all_or_none {
 	load_texture: proc(data: []u8, width: int, height: int, format: Pixel_Format) -> Texture_Handle,
 	update_texture: proc(handle: Texture_Handle, data: []u8, rect: Rect) -> bool,
 	destroy_texture: proc(handle: Texture_Handle),
+	texture_needs_vertical_flip: proc(handle: Texture_Handle) -> bool,
 
 	create_render_texture: proc(width: int, height: int) -> (Texture_Handle, Render_Target_Handle),
 	destroy_render_target: proc(render_texture: Render_Target_Handle),