#ifndef video_hpp #define video_hpp #include #include extern "C" { #include "sc/sh_enums.h" #include "vid_enums.h" } #include "asset.hpp" struct App; struct Arena; struct Heap; template struct Primitive_Id { T index; Primitive_Id(): index(0) {} Primitive_Id(T i): index(i) {} bool operator==(Primitive_Id other) const { return index == other.index; } bool operator!=(Primitive_Id other) const { return index != other.index; } operator bool() const { return index != 0; } }; struct Texture_Id : public Primitive_Id { using Primitive_Id::Primitive_Id; }; struct Buffer_Id : public Primitive_Id { using Primitive_Id::Primitive_Id; }; struct Vertex_Format_Id : public Primitive_Id { using Primitive_Id::Primitive_Id; }; struct Shader_Id : public Primitive_Id { using Primitive_Id::Primitive_Id; }; struct Sampler_Id : public Primitive_Id { using Primitive_Id::Primitive_Id; }; #define descriptor_payload_size 16 struct Descriptor { enum class Type { texture, constant_buffer } type; int slot; uint8_t payload[descriptor_payload_size]; }; struct Texture_Descriptor { Texture_Id texture; Sampler_Id sampler; }; struct Constant_Buffer_Descriptor { int offset, size; Buffer_Id buffer; }; static_assert(sizeof(Texture_Descriptor) <= descriptor_payload_size); static_assert(sizeof(Constant_Buffer_Descriptor) <= descriptor_payload_size); #define pipeline_max_descriptors 16 enum class Depth_Mode { less, less_equal, equal, greater, greater_equal, always, never }; enum class Blend_Factor { zero, one, src_colour, inv_src_colour, dst_colour, inv_dst_colour, src_alpha, inv_src_alpha, dst_alpha, inv_dst_alpha }; enum class Blend_Mode { add, subtract, reverse_subtract, min, max }; enum class Cull_Mode { none, back, front }; struct Pipeline { uint64_t pipeline_hash; uint64_t descriptor_resource_hash; int viewport[4]; int scissor[4]; bool depth_test, depth_write; bool blend_enable; Depth_Mode depth_mode; Blend_Factor blend_src, blend_dst; Blend_Mode blend_mode; Blend_Factor blend_src_alpha, blend_dst_alpha; Blend_Mode blend_mode_alpha; Cull_Mode cull_mode; Vertex_Format_Id vertex_format; Shader_Id shader; Descriptor descriptors[pipeline_max_descriptors]; int descriptor_count; void hash(); bool pipeline_eq(const Pipeline& other) const { return shader == other.shader && vertex_format == other.vertex_format && viewport[0] == other.viewport[0] && viewport[1] == other.viewport[1] && viewport[2] == other.viewport[2] && viewport[3] == other.viewport[3] && scissor[0] == other.scissor[0] && scissor[1] == other.scissor[1] && scissor[2] == other.scissor[2] && scissor[3] == other.scissor[3] && depth_test == other.depth_test && depth_write == other.depth_write && depth_mode == other.depth_mode && blend_enable == other.blend_enable && blend_src == other.blend_src && blend_dst == other.blend_dst && blend_src_alpha == other.blend_src_alpha && blend_dst_alpha == other.blend_dst_alpha && blend_mode == other.blend_mode && blend_mode_alpha == other.blend_mode_alpha && cull_mode == other.cull_mode; } bool desc_layout_eq(const Pipeline& other) const { int i, c = descriptor_count; if (other.pipeline_hash != pipeline_hash) return false; if (other.descriptor_count != c) return false; for (i = 0; i < c; i++) { auto& a = descriptors[i]; auto& b = other.descriptors[i]; if (a.type != b.type) return false; if (a.slot != b.slot) return false; } return true; } bool desc_resources_eq(const Pipeline& other) const { int i, c = descriptor_count; if (other.descriptor_resource_hash != descriptor_resource_hash) return false; if (other.descriptor_count != c) return false; for (i = 0; i < c; i++) { auto& a = descriptors[i]; auto& b = other.descriptors[i]; if (a.type != b.type) return false; if (a.slot != b.slot) return false; switch (a.type) { case Descriptor::Type::texture: { Texture_Descriptor* ta = (Texture_Descriptor*)a.payload; Texture_Descriptor* tb = (Texture_Descriptor*)b.payload; if (ta->texture != tb->texture) return false; if (ta->sampler != tb->sampler) return false; } break; case Descriptor::Type::constant_buffer: { Constant_Buffer_Descriptor* ca = (Constant_Buffer_Descriptor*)a.payload; Constant_Buffer_Descriptor* cb = (Constant_Buffer_Descriptor*)b.payload; if (ca->buffer != cb->buffer) return false; if (ca->size != cb->size) return false; if (ca->offset != cb->offset) return false; } break; } } return true; } }; enum class Clear_Mode { discard, clear, restore }; struct Colour { uint8_t r, g, b, a; }; #define max_colour_attachments 8 struct Render_Pass { struct Target { Texture_Id id; Texture_Format fmt; Clear_Mode mode; union { Colour colour; float depth; } clear; }; Target colours[max_colour_attachments]; int colour_count; Target depth; uint64_t layout_hash; uint64_t resource_hash; bool layout_eq(const Render_Pass& other) const { int i, c = colour_count; if (layout_hash != other.layout_hash) return false; if (c != other.colour_count) return false; if (depth.fmt != other.depth.fmt) return false; if (depth.mode != other.depth.mode) return false; for (i = 0; i < c; i++) { const Target& ac = colours[i]; const Target& bc = other.colours[i]; if (ac.fmt != bc.fmt) return false; if (ac.mode != bc.mode) return false; } return true; } bool resources_eq(const Render_Pass& other) const { int i, c = colour_count; if (resource_hash != other.resource_hash) return false; if (!layout_eq(other)) return false; for (i = 0; i < c; i++) { const Target& ac = colours[i]; const Target& bc = other.colours[i]; if (ac.id != bc.id) return false; } return true; } }; struct Vertex_Buffer_Binding { Buffer_Id id; size_t offset; int target; }; struct Index_Buffer_Binding { Buffer_Id id; size_t offset; }; struct Draw { Vertex_Buffer_Binding* verts; Index_Buffer_Binding inds; int vertex_count; int instance_count; int first_vertex; int first_instance; int vertex_offset; }; struct Device; struct Pipeline_Builder { Arena* arena; Render_Pass* pass; Pipeline* pip; Device* dev; Pipeline_Builder(Arena* arena, Device* dev); void begin_rp(); void rp_target(Texture_Id id, Colour clear_colour); void rp_target(Texture_Id id, Clear_Mode mode); void rp_depth_target(Texture_Id id, float clear_depth); void rp_depth_target(Texture_Id id, Clear_Mode mode); Render_Pass& build_rp(); void validate_rp(); void begin(); void viewport(int x, int y, int w, int h); void scissor(int x, int y, int w, int h); void depth(bool test, bool write, Depth_Mode mode); void blend(Blend_Mode mode, Blend_Factor src, Blend_Factor dst); void blend( Blend_Mode mode_col, Blend_Factor src_col, Blend_Factor dst_col, Blend_Mode mode_alpha, Blend_Factor src_alpha, Blend_Factor dst_alpha ); void cull(Cull_Mode mode); void shader(Shader_Id s); void vertex_format(Vertex_Format_Id vf); void texture(int binding, Texture_Id t, Sampler_Id s); void cbuffer( int binding, Buffer_Id id, int offset = 0, int size = 0 ); Pipeline& build(); void validate(); }; enum Resource_State { undefined, copy_dst, copy_src, shader_read, render_target, presentable }; struct Texture : public Asset { Texture_Id id; Texture_Format fmt; int flags; int w, h, d; int mip_count, array_size; int start_mip, start_array; bool alias; }; namespace Buffer_Flags { enum { index_buffer = 1 << 0, vertex_buffer = 1 << 1, constant_buffer = 1 << 2, storage_buffer = 1 << 3, cpu_read = 1 << 4, cpu_readwrite = 1 << 5, copy_src = 1 << 6, copy_dst = 1 << 7 }; }; namespace Texture_Flags { enum { sampleable = 1 << 0, colour_target = 1 << 1, depth_stencil_target = 1 << 2, copy_src = 1 << 3, copy_dst = 1 << 4, cubemap = 1 << 5, swapchain = 1 << 6 }; }; struct Buffer { Buffer_Id id; }; enum class Filter_Mode { point, linear }; enum class Address_Mode { repeat, mirror, clamp, border }; struct Sampler_State { Filter_Mode min; Filter_Mode mag; Filter_Mode mip; Address_Mode address_u; Address_Mode address_v; Address_Mode address_w; float border[4]; float mip_bias; bool aniso; }; struct Vertex_Format_Desc { struct Binding { int binding; int stride; SBinding_Rate rate; }; struct Attribute { int binding; int index; int offset; SVariable_Type type; }; Binding* bindings; Attribute* attributes; int binding_count; int attribute_count; }; struct Device; struct Context; struct Device_Debug_Hooks { Device* dev; virtual void on_rpo_create(const Render_Pass& rpo); virtual void on_rpo_destroy(const Render_Pass& rpo); virtual void on_fbo_create(const Render_Pass& pass); virtual void on_fbo_destroy(const Render_Pass& pass); virtual void on_pso_create(const Pipeline& pso); virtual void on_pso_destroy(const Pipeline& pso); virtual void on_dso_create(const Pipeline& pso); virtual void on_dso_destroy(const Pipeline& pso); virtual void on_acquire(Context& ctx); virtual void on_submit(Context& ctx); virtual void on_present(Context& ctx); virtual void on_page_alloc(size_t size); virtual void on_vram_alloc(size_t size, size_t align); }; struct Context; struct Shader; struct Device { Arena* arena; Heap* heap; App* app; Device_Debug_Hooks* hooks; static Device* create(Arena* a, App* ap); void init(Arena* a, App* ap); void destroy(); void register_hooks(Device_Debug_Hooks* hooks); void on_resize(); void begin_frame(); void submit(Context& ctx); void present(); Context& acquire(); Context& get_ctx(); Texture_Id create_texture( const char* name, Texture_Format fmt, int flags, int w, int h, int d, int mip_count, int array_size, Buffer_Id init ); Texture_Id alias_texture( Texture_Id o, const char* name, Texture_Format fmt, int flags, int w, int h, int d, int mip_count, int array_size, int start_mip, int start_array ); Texture_Id get_backbuffer(); Texture_Id get_depth_target(); Texture& get_texture(Texture_Id id); void destroy_texture(Texture_Id id); void destroy_texturei(Texture_Id id); Buffer_Id create_buffer( const char* name, size_t size, int flags ); void* map_buffer(Buffer_Id id, size_t offset, size_t size); void unmap_buffer(Buffer_Id id); Buffer& get_buffer(Buffer_Id id); void destroy_buffer(Buffer_Id id); void destroy_bufferi(Buffer_Id id); Vertex_Format_Id create_vertex_format( const Vertex_Format_Desc& desc ); void destroy_vertex_format(Vertex_Format_Id fmt); Shader& get_shader(Shader_Id id); Sampler_Id create_sampler( const char* name, const Sampler_State& state ); void destroy_sampler(Sampler_Id id); void destroy_sampleri(Sampler_Id id); }; struct Context { void wait(); void submit( const Draw& draw, const Pipeline& p, const Render_Pass& rp ); void submit( const Draw* draws, int count, const Pipeline& p, const Render_Pass& rp ); void submit(const Render_Pass& rp); void copy(Buffer_Id dst, Buffer_Id src); void copy(Texture_Id dst, Buffer_Id src); void transition(Texture_Id id, Resource_State state); void debug_push(const char* name); void debug_pop(); }; struct Shader : public Asset { Shader_Id id; Vertex_Format_Id vf; void destroy(Device* dev); /* -1 on failure */ int binding_index(const char* name); int attribute_index(const char* name); int target_index(const char* name); int descriptor_binding(const char* name); int descriptor_stage(int slot); }; struct Staged_Buffer { Buffer_Id stage; Buffer_Id gpuonly; int size; void init( Device* dev, const char* name, int size, int flags ); void destroy(Device* dev); void* map(Device* dev); void unmap(Device* dev); void update(Context& ctx); }; #endif