diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f0c221..67a1501 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,12 +103,13 @@ add_library(blah private/blah/third_party/stb_truetype.h private/blah/internal/graphics_backend.h private/blah/internal/graphics_backend_gl.cpp + private/blah/internal/graphics_backend_d3d11.cpp private/blah/internal/input_backend.h private/blah/internal/platform_backend.h private/blah/internal/platform_backend_sdl2.cpp ) -target_include_directories(blah +target_include_directories(blah PUBLIC $ PRIVATE @@ -119,16 +120,27 @@ target_include_directories(blah set(SDL2_ENABLED true CACHE BOOL "Use SDL2 as the System implementation") set(SDL2_INCLUDE_DIRS "" CACHE FILEPATH "SDL2 Headers") set(SDL2_LIBRARIES "" CACHE FILEPATH "SDL2 Headers") -set(OPENGL_ENABLED true CACHE BOOL "Include OpenGL graphics implementation") +set(OPENGL_ENABLED true CACHE BOOL "Use OpenGL graphics implementation") +set(D3D11_ENABLED false CACHE BOOL "Use D3D11 graphics implementation") + +set(LIBS "") # add OpenGL definition if we're using OpenGL if (OPENGL_ENABLED) add_compile_definitions(BLAH_USE_OPENGL) endif() +# add D3D11 definition if we're using OpenGL +if (D3D11_ENABLED) + add_compile_definitions(BLAH_USE_D3D11) + set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib) +endif() + # Link and create SDL2 Definition if we're using SDL2 if (SDL2_ENABLED) add_compile_definitions(BLAH_USE_SDL2) target_include_directories(blah PUBLIC "$") - target_link_libraries(blah PUBLIC ${SDL2_LIBRARIES}) -endif() \ No newline at end of file + set(LIBS ${LIBS} ${SDL2_LIBRARIES}) +endif() + +target_link_libraries(blah PUBLIC ${LIBS}) \ No newline at end of file diff --git a/private/blah/internal/graphics_backend_d3d11.cpp b/private/blah/internal/graphics_backend_d3d11.cpp new file mode 100644 index 0000000..b561e5a --- /dev/null +++ b/private/blah/internal/graphics_backend_d3d11.cpp @@ -0,0 +1,1255 @@ +#ifdef BLAH_USE_D3D11 + +#include +#include +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +namespace Blah +{ + class D3D11_Shader; + + struct D3D11 + { + ID3D11Device* device = nullptr; + ID3D11DeviceContext* context = nullptr; + IDXGISwapChain* swap_chain = nullptr; + ID3D11RenderTargetView* backbuffer = nullptr; + ID3D11RasterizerState* rasterizer = nullptr; + ID3D11SamplerState* sampler = nullptr; + ID3D11DepthStencilState* depth_state = nullptr; + RendererFeatures features; + Point last_size; + + struct InputLayoutEntry + { + uint32_t shader_hash; + VertexFormat format; + ID3D11InputLayout* layout; + }; + + struct BlendStateEntry + { + BlendMode blend; + ID3D11BlendState* state; + }; + + Vector layout_cache; + Vector blend_cache; + + ID3D11InputLayout* get_layout(D3D11_Shader* shader, const VertexFormat& format); + ID3D11BlendState* get_blend(const BlendMode& blend); + }; + + D3D11 state; + + D3D11_BLEND_OP blend_op(BlendOp op) + { + switch (op) + { + case BlendOp::Add: return D3D11_BLEND_OP_ADD; + case BlendOp::Subtract: return D3D11_BLEND_OP_SUBTRACT; + case BlendOp::ReverseSubtract: return D3D11_BLEND_OP_REV_SUBTRACT; + case BlendOp::Min: return D3D11_BLEND_OP_MIN; + case BlendOp::Max: return D3D11_BLEND_OP_MAX; + } + + return D3D11_BLEND_OP_ADD; + } + + D3D11_BLEND blend_factor(BlendFactor factor) + { + switch (factor) + { + case BlendFactor::Zero: return D3D11_BLEND_ZERO; + case BlendFactor::One: return D3D11_BLEND_ONE; + case BlendFactor::SrcColor: return D3D11_BLEND_SRC_COLOR; + case BlendFactor::OneMinusSrcColor: return D3D11_BLEND_INV_SRC_COLOR; + case BlendFactor::DstColor: return D3D11_BLEND_DEST_COLOR; + case BlendFactor::OneMinusDstColor: return D3D11_BLEND_INV_DEST_COLOR; + case BlendFactor::SrcAlpha: return D3D11_BLEND_SRC_ALPHA; + case BlendFactor::OneMinusSrcAlpha: return D3D11_BLEND_INV_SRC_ALPHA; + case BlendFactor::DstAlpha: return D3D11_BLEND_DEST_ALPHA; + case BlendFactor::OneMinusDstAlpha: return D3D11_BLEND_INV_DEST_ALPHA; + case BlendFactor::ConstantColor: return D3D11_BLEND_BLEND_FACTOR; + case BlendFactor::OneMinusConstantColor: return D3D11_BLEND_INV_BLEND_FACTOR; + case BlendFactor::ConstantAlpha: return D3D11_BLEND_BLEND_FACTOR; + case BlendFactor::OneMinusConstantAlpha: return D3D11_BLEND_INV_BLEND_FACTOR; + case BlendFactor::SrcAlphaSaturate: return D3D11_BLEND_SRC_ALPHA_SAT; + case BlendFactor::Src1Color: return D3D11_BLEND_SRC1_COLOR; + case BlendFactor::OneMinusSrc1Color: return D3D11_BLEND_INV_SRC1_COLOR; + case BlendFactor::Src1Alpha: return D3D11_BLEND_SRC1_ALPHA; + case BlendFactor::OneMinusSrc1Alpha: return D3D11_BLEND_INV_SRC1_ALPHA; + } + + return D3D11_BLEND_ZERO; + } + + bool reflect_uniforms(Vector& append_to, Vector& buffers, ID3DBlob* shader, ShaderType shader_type) + { + ID3D11ShaderReflection* reflector = nullptr; + D3DReflect(shader->GetBufferPointer(), shader->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&reflector); + + D3D11_SHADER_DESC shader_desc; + reflector->GetDesc(&shader_desc); + + for (int i = 0; i < shader_desc.BoundResources; i++) + { + D3D11_SHADER_INPUT_BIND_DESC desc; + reflector->GetResourceBindingDesc(i, &desc); + + if (desc.Type == D3D_SIT_TEXTURE && + desc.Dimension == D3D_SRV_DIMENSION_TEXTURE2D) + { + auto uniform = append_to.expand(); + uniform->name = desc.Name; + uniform->shader = shader_type; + uniform->buffer_index = 0; + uniform->array_length = max(1, desc.BindCount); + uniform->type = UniformType::Texture; + } + } + + for (int i = 0; i < shader_desc.ConstantBuffers; i++) + { + D3D11_SHADER_BUFFER_DESC desc; + + auto cb = reflector->GetConstantBufferByIndex(i); + cb->GetDesc(&desc); + + // create the constant buffer for assigning data later + { + D3D11_BUFFER_DESC buffer_desc = {}; + buffer_desc.ByteWidth = desc.Size; + buffer_desc.Usage = D3D11_USAGE_DYNAMIC; + buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + ID3D11Buffer* buffer; + state.device->CreateBuffer(&buffer_desc, nullptr, &buffer); + buffers.push_back(buffer); + } + + // get the uniforms + for (int j = 0; j < desc.Variables; j++) + { + D3D11_SHADER_VARIABLE_DESC var_desc; + D3D11_SHADER_TYPE_DESC type_desc; + + auto var = cb->GetVariableByIndex(j); + var->GetDesc(&var_desc); + + auto type = var->GetType(); + type->GetDesc(&type_desc); + + auto uniform = append_to.expand(); + uniform->name = var_desc.Name; + uniform->shader = shader_type; + uniform->buffer_index = i; + uniform->array_length = max(1, type_desc.Elements); + uniform->type = UniformType::None; + + if (type_desc.Type == D3D_SVT_FLOAT) + { + if (type_desc.Rows == 1) + { + if (type_desc.Columns == 1) + uniform->type = UniformType::Float; + else if (type_desc.Columns == 2) + uniform->type = UniformType::Float2; + else if (type_desc.Columns == 3) + uniform->type = UniformType::Float3; + else if (type_desc.Columns == 4) + uniform->type = UniformType::Float4; + } + else if (type_desc.Rows == 2 && type_desc.Columns == 3) + { + uniform->type = UniformType::Mat3x2; + } + else if (type_desc.Rows == 4 && type_desc.Columns == 4) + { + uniform->type = UniformType::Mat4x4; + } + } + } + } + + return true; + } + + class D3D11_Texture : public Texture + { + private: + int m_width; + int m_height; + TextureFilter m_filter; + TextureFormat m_format; + TextureWrap m_wrap_x; + TextureWrap m_wrap_y; + bool m_is_framebuffer; + int m_size; + + public: + ID3D11Texture2D* texture = nullptr; + ID3D11ShaderResourceView* view = nullptr; + + D3D11_Texture(int width, int height, TextureFilter filter, TextureWrap wrap_x, TextureWrap wrap_y, TextureFormat format, bool is_framebuffer) + { + m_width = width; + m_height = height; + m_filter = filter; + m_format = format; + m_wrap_x = wrap_x; + m_wrap_y = wrap_y; + m_is_framebuffer = is_framebuffer; + m_size = 0; + + D3D11_TEXTURE2D_DESC desc = { 0 }; + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + if (is_framebuffer) + desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + + switch (format) + { + case TextureFormat::R: + desc.Format = DXGI_FORMAT_R8_UNORM; + m_size = width * height; + break; + case TextureFormat::RG: + desc.Format = DXGI_FORMAT_R8G8_UNORM; + m_size = width * height * 2; + break; + case TextureFormat::RGBA: + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + m_size = width * height * 4; + break; + case TextureFormat::DepthStencil: + desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + m_size = width * height * 4; + break; + } + + auto hr = state.device->CreateTexture2D(&desc, NULL, &texture); + if (!SUCCEEDED(hr)) + { + if (texture) + texture->Release(); + texture = nullptr; + return; + } + + hr = state.device->CreateShaderResourceView(texture, NULL, &view); + if (!SUCCEEDED(hr)) + { + texture->Release(); + texture = nullptr; + } + } + + ~D3D11_Texture() + { + if (texture) + texture->Release(); + texture = nullptr; + } + + virtual int width() const override + { + return m_width; + } + + virtual int height() const override + { + return m_height; + } + + virtual TextureFormat format() const override + { + return m_format; + } + + virtual void set_filter(TextureFilter filter) override + { + m_filter = filter; + } + + virtual TextureFilter get_filter() const override + { + return m_filter; + } + + virtual void set_wrap(TextureWrap x, TextureWrap y) override + { + m_wrap_x = x; + m_wrap_y = y; + } + + virtual TextureWrap get_wrap_x() const override + { + return m_wrap_x; + } + + virtual TextureWrap get_wrap_y() const override + { + return m_wrap_y; + } + + virtual void set_data(unsigned char* data) override + { + D3D11_BOX box; + box.left = 0; + box.right = m_width; + box.top = 0; + box.bottom = m_height; + box.front = 0; + box.back = 1; + + state.context->UpdateSubresource( + texture, + 0, + &box, + data, + m_size / m_height, + 0); + } + + virtual void get_data(unsigned char* data) override + { + + } + + virtual bool is_framebuffer() const override + { + return m_is_framebuffer; + } + + }; + + class D3D11_FrameBuffer : public FrameBuffer + { + private: + Attachments m_attachments; + + public: + StackVector color_views; + ID3D11DepthStencilView* depth_view = nullptr; + + D3D11_FrameBuffer(int width, int height, const TextureFormat* attachments, int attachmentCount) + { + for (int i = 0; i < attachmentCount; i++) + { + auto tex = new D3D11_Texture( + width, height, + TextureFilter::Linear, + TextureWrap::Repeat, + TextureWrap::Repeat, + attachments[i], + true); + m_attachments.push_back(TextureRef(tex)); + + if (attachments[i] == TextureFormat::DepthStencil) + { + state.device->CreateDepthStencilView(tex->texture, nullptr, &depth_view); + } + else + { + ID3D11RenderTargetView* view = nullptr; + state.device->CreateRenderTargetView(tex->texture, nullptr, &view); + color_views.push_back(view); + } + } + } + + ~D3D11_FrameBuffer() + { + for (auto& it : color_views) + it->Release(); + color_views.clear(); + } + + virtual Attachments& attachments() override + { + return m_attachments; + } + + virtual const Attachments& attachments() const override + { + return m_attachments; + } + + virtual TextureRef& attachment(int index) override + { + return m_attachments[index]; + } + + virtual const TextureRef& attachment(int index) const override + { + return m_attachments[index]; + } + + virtual int width() const override + { + return m_attachments[0]->width(); + } + + virtual int height() const override + { + return m_attachments[0]->height(); + } + + virtual void clear(Color color) override + { + float col[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; + + for (int i = 0; i < m_attachments.size(); i++) + state.context->ClearRenderTargetView(color_views[i], col); + } + }; + + class D3D11_Shader : public Shader + { + public: + ID3D11VertexShader* vertex = nullptr; + ID3D11PixelShader* fragment = nullptr; + ID3DBlob* vertex_blob = nullptr; + ID3DBlob* fragment_blob = nullptr; + Vector vcb; + Vector fcb; + StackVector attributes; + Vector uniform_list; + uint32_t hash = 0; + + D3D11_Shader(const ShaderData* data) + { + UINT flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_DEBUG; + ID3DBlob* error_blob = nullptr; + HRESULT hr; + + // compile vertex shader + { + hr = D3DCompile( + data->vertex, + strlen(data->vertex), + nullptr, + nullptr, + nullptr, + "vs_main", + "vs_5_0", + flags, + 0, + &vertex_blob, + &error_blob); + + if (FAILED(hr)) + { + if (error_blob) + { + Log::error("%s", (char*)error_blob->GetBufferPointer()); + error_blob->Release(); + } + + return; + } + } + + // compile fragment shader + { + hr = D3DCompile( + data->fragment, + strlen(data->fragment), + nullptr, + nullptr, + nullptr, + "ps_main", + "ps_5_0", + flags, + 0, + &fragment_blob, + &error_blob); + + if (FAILED(hr)) + { + if (error_blob) + { + Log::error("%s", (char*)error_blob->GetBufferPointer()); + error_blob->Release(); + } + + return; + } + } + + // create vertex shader + { + hr = state.device->CreateVertexShader( + vertex_blob->GetBufferPointer(), + vertex_blob->GetBufferSize(), + NULL, + &vertex); + + if (!SUCCEEDED(hr)) + return; + } + + // create fragment shader + { + hr = state.device->CreatePixelShader( + fragment_blob->GetBufferPointer(), + fragment_blob->GetBufferSize(), + NULL, + &fragment); + + if (!SUCCEEDED(hr)) + return; + } + + // get uniforms + reflect_uniforms(uniform_list, vcb, vertex_blob, ShaderType::Vertex); + reflect_uniforms(uniform_list, fcb, fragment_blob, ShaderType::Fragment); + + // combine uniforms that were in both + for (int i = 0; i < uniform_list.size(); i++) + { + for (int j = i + 1; j < uniform_list.size(); j++) + { + if (strcmp(uniform_list[i].name, uniform_list[j].name) == 0) + { + if (uniform_list[i].type == uniform_list[j].type) + { + uniform_list[i].shader = (ShaderType)((int)uniform_list[i].shader | (int)uniform_list[j].shader); + uniform_list.erase(j); + j--; + } + else + { + // TODO: + // We don't allow uniforms to share names ... + // This should result in an invalid shader + } + } + } + } + + // TODO: + // Validate Uniforms! Make sure they're all types we understand + // (ex. float/matrix only) + + // copy HLSL attributes + attributes = data->hlsl_attributes; + + // store hash + hash = 5381; + for (auto& it : attributes) + { + for (int i = 0, n = strlen(it.semantic_name); i < n; i++) + hash = ((hash << 5) + hash) + it.semantic_name[i]; + hash = it.semantic_index << 5 + hash; + } + } + + ~D3D11_Shader() + { + if (vertex) vertex->Release(); + if (vertex_blob) vertex_blob->Release(); + if (fragment) fragment->Release(); + if (fragment_blob) fragment_blob->Release(); + + vertex = nullptr; + vertex_blob = nullptr; + fragment = nullptr; + fragment_blob = nullptr; + } + + virtual Vector& uniforms() override + { + return uniform_list; + } + + virtual const Vector& uniforms() const override + { + return uniform_list; + } + }; + + class D3D11_Mesh : public Mesh + { + private: + int64_t m_vertex_count = 0; + int64_t m_vertex_capacity = 0; + int64_t m_index_count = 0; + int64_t m_index_capacity = 0; + + public: + ID3D11Buffer* vertex_buffer = nullptr; + VertexFormat vertex_format; + ID3D11Buffer* index_buffer = nullptr; + IndexFormat index_format = IndexFormat::UInt16; + int index_stride = 0; + + D3D11_Mesh() + { + + } + + ~D3D11_Mesh() + { + if (vertex_buffer) + vertex_buffer->Release(); + if (index_buffer) + index_buffer->Release(); + } + + virtual void index_data(IndexFormat format, const void* indices, int64_t count) override + { + m_index_count = count; + + if (index_format != format || !index_buffer || m_index_count > m_index_capacity) + { + index_stride = 0; + index_format = format; + m_index_capacity = max(m_index_capacity, m_index_count); + + switch (format) + { + case IndexFormat::UInt16: index_stride = sizeof(int16_t); break; + case IndexFormat::UInt32: index_stride = sizeof(int32_t); break; + } + + if (m_index_capacity > 0 && indices) + { + // release existing buffer + if (index_buffer) + index_buffer->Release(); + index_buffer = nullptr; + + // buffer description + D3D11_BUFFER_DESC desc = { 0 }; + desc.ByteWidth = index_stride * m_index_capacity; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + // buffer data + D3D11_SUBRESOURCE_DATA data = { 0 }; + data.pSysMem = indices; + + // create + state.device->CreateBuffer(&desc, &data, &index_buffer); + } + } + else if (indices) + { + D3D11_MAPPED_SUBRESOURCE resource; + state.context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource); + memcpy(resource.pData, indices, index_stride * count); + state.context->Unmap(index_buffer, 0); + } + } + + virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) override + { + m_vertex_count = count; + + // recreate buffer if we've changed + if (vertex_format.stride != format.stride || !vertex_buffer || m_vertex_count > m_vertex_capacity) + { + m_vertex_capacity = max(m_vertex_capacity, m_vertex_count); + vertex_format = format; + + // discard old buffer + if (vertex_buffer) + vertex_buffer->Release(); + vertex_buffer = nullptr; + + if (m_vertex_capacity > 0 && vertices) + { + // buffer description + D3D11_BUFFER_DESC desc = { 0 }; + desc.ByteWidth = format.stride * m_vertex_capacity; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + // buffer data + D3D11_SUBRESOURCE_DATA data = { 0 }; + data.pSysMem = vertices; + + // create + state.device->CreateBuffer(&desc, &data, &vertex_buffer); + } + } + // otherwise just update it + else if (vertices) + { + D3D11_MAPPED_SUBRESOURCE resource; + state.context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource); + memcpy(resource.pData, vertices, vertex_format.stride * count); + state.context->Unmap(vertex_buffer, 0); + } + } + + virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) override + { + + } + + virtual int64_t index_count() const override + { + return m_index_count; + } + + virtual int64_t vertex_count() const override + { + return m_vertex_count; + } + + virtual int64_t instance_count() const override + { + return 0; + } + }; + + bool GraphicsBackend::init() + { + state = D3D11(); + state.last_size = Point(App::draw_width(), App::draw_height()); + + DXGI_SWAP_CHAIN_DESC desc = { 0 }; + desc.BufferDesc.RefreshRate.Numerator = 0; + desc.BufferDesc.RefreshRate.Denominator = 1; + desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = 2; + desc.OutputWindow = (HWND)PlatformBackend::d3d11_get_hwnd(); + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.Windowed = true; + + D3D_FEATURE_LEVEL feature_level; + UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_DEBUG; + HRESULT hr = D3D11CreateDeviceAndSwapChain( + NULL, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + flags, + NULL, + 0, + D3D11_SDK_VERSION, + &desc, + &state.swap_chain, + &state.device, + &feature_level, + &state.context); + + if (hr != S_OK || !state.swap_chain || !state.device || !state.context) + return false; + + // get the backbuffer + ID3D11Texture2D* frame_buffer = nullptr; + state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); + if (frame_buffer) + { + state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer); + frame_buffer->Release(); + } + + state.features.instancing = true; + state.features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + state.features.origin_bottom_left = false; + + return true; + } + + Renderer GraphicsBackend::renderer() + { + return Renderer::D3D11; + } + + void GraphicsBackend::shutdown() + { + state.swap_chain->Release(); + state.context->Release(); + state.device->Release(); + } + + const RendererFeatures& GraphicsBackend::features() + { + return state.features; + } + + void GraphicsBackend::frame() + { + } + + void GraphicsBackend::before_render() + { + auto next_size = Point(App::draw_width(), App::draw_height()); + if (state.last_size != next_size) + { + // release old buffer + if (state.backbuffer) + state.backbuffer->Release(); + + // perform resize + state.swap_chain->ResizeBuffers(2, next_size.x, next_size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + state.last_size = next_size; + + // get the new buffer + ID3D11Texture2D* frame_buffer = nullptr; + state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); + if (frame_buffer) + { + state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer); + frame_buffer->Release(); + } + } + } + + void GraphicsBackend::after_render() + { + state.swap_chain->Present(1, 0); + } + + TextureRef GraphicsBackend::create_texture(int width, int height, TextureFilter filter, TextureWrap wrap_x, TextureWrap wrap_y, TextureFormat format) + { + auto result = new D3D11_Texture(width, height, filter, wrap_x, wrap_y, format, false); + if (result->texture) + return TextureRef(result); + + delete result; + return TextureRef(); + } + + FrameBufferRef GraphicsBackend::create_framebuffer(int width, int height, const TextureFormat* attachments, int attachmentCount) + { + return FrameBufferRef(new D3D11_FrameBuffer(width, height, attachments, attachmentCount)); + } + + ShaderRef GraphicsBackend::create_shader(const ShaderData* data) + { + auto result = new D3D11_Shader(data); + if (result->vertex && result->fragment && result->vertex_blob && result->fragment_blob) + return ShaderRef(result); + + delete result; + return ShaderRef(); + } + + MeshRef GraphicsBackend::create_mesh() + { + return MeshRef(new D3D11_Mesh()); + } + + void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type) + { + // HACK: + // Apply Uniforms ... I don't like how this is set up at all! This needs to be better!! + // The fact it builds this every render call is UGLY + + auto& buffers = (type == ShaderType::Vertex ? shader->vcb : shader->fcb); + + Vector buffer_input; + for (int i = 0; i < buffers.size(); i++) + { + // build block + const float* data = material->data(); + for (auto& it : shader->uniforms()) + { + if (it.type == UniformType::Texture) + continue; + + int size = 0; + switch (it.type) + { + case UniformType::Float: size = 1; break; + case UniformType::Float2: size = 2; break; + case UniformType::Float3: size = 3; break; + case UniformType::Float4: size = 4; break; + case UniformType::Mat3x2: size = 6; break; + case UniformType::Mat4x4: size = 16; break; + } + + int length = size * it.array_length; + + if (it.buffer_index == i && ((int)it.shader & (int)type) != 0) + { + auto start = buffer_input.expand(length); + memcpy(start, data, sizeof(float) * length); + } + + data += length; + } + + // apply block + if (buffers[i]) + { + D3D11_MAPPED_SUBRESOURCE map; + state.context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, buffer_input.begin(), buffer_input.size() * sizeof(float)); + state.context->Unmap(buffers[i], 0); + } + + // clear for next buffer + buffer_input.clear(); + } + } + + void GraphicsBackend::render(const RenderPass& pass) + { + auto ctx = state.context; + auto mesh = (D3D11_Mesh*)pass.mesh.get(); + auto shader = (D3D11_Shader*)(pass.material->shader().get()); + + // OM + { + // Set the Target + if (pass.target == App::backbuffer || !pass.target) + { + ctx->OMSetRenderTargets(1, &state.backbuffer, nullptr); + } + else + { + auto target = (D3D11_FrameBuffer*)(pass.target.get()); + ctx->OMSetRenderTargets(target->color_views.size(), target->color_views.begin(), target->depth_view); + } + + // Depth + // TODO: Doesn't actually assign proper values + // TODO: Cache this + { + if (state.depth_state) + state.depth_state->Release(); + + D3D11_DEPTH_STENCIL_DESC desc = {}; + desc.DepthEnable = FALSE; + desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + desc.DepthFunc = D3D11_COMPARISON_NEVER; + + state.device->CreateDepthStencilState(&desc, &state.depth_state); + ctx->OMSetDepthStencilState(state.depth_state, 0); + } + + // Blend Mode + { + auto blend = state.get_blend(pass.blend); + auto color = Color::from_rgba(pass.blend.rgba); + float factor[4]{ color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; + auto mask = 0xffffffff; + ctx->OMSetBlendState(blend, factor, mask); + } + } + + // IA + { + // We draw triangles + ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + // Assign Layout + auto layout = state.get_layout(shader, mesh->vertex_format); + ctx->IASetInputLayout(layout); + + // Assign Vertex Buffer + { + UINT stride = mesh->vertex_format.stride; + UINT offset = 0; + + ctx->IASetVertexBuffers( + 0, + 1, + &mesh->vertex_buffer, + &stride, + &offset); + + D3D11_BUFFER_DESC desc; + mesh->vertex_buffer->GetDesc(&desc); + } + + // Assign Index Buffer + { + DXGI_FORMAT format = DXGI_FORMAT_R16_UINT; + + switch (mesh->index_format) + { + case IndexFormat::UInt16: format = DXGI_FORMAT_R16_UINT; break; + case IndexFormat::UInt32: format = DXGI_FORMAT_R32_UINT; break; + } + + ctx->IASetIndexBuffer(mesh->index_buffer, format, 0); + } + } + + // VS + { + apply_uniforms(shader, pass.material, ShaderType::Vertex); + ctx->VSSetShader(shader->vertex, nullptr, 0); + ctx->VSSetConstantBuffers(0, shader->vcb.size(), shader->vcb.begin()); + } + + // PS + { + apply_uniforms(shader, pass.material, ShaderType::Fragment); + ctx->PSSetShader(shader->fragment, nullptr, 0); + ctx->PSSetConstantBuffers(0, shader->fcb.size(), shader->fcb.begin()); + + // Fragment Shader Textures + auto& textures = pass.material->textures(); + for (int i = 0; i < textures.size(); i++) + { + if (textures[i]) + { + auto view = ((D3D11_Texture*)textures[i].get())->view; + ctx->PSSetShaderResources(i, 1, &view); + } + } + + // Sampler + // TODO: Doesn't actually assign proper values + // TODO: Cache this + { + if (state.sampler) + state.sampler->Release(); + + D3D11_SAMPLER_DESC samplerDesc = {}; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + + state.device->CreateSamplerState(&samplerDesc, &state.sampler); + ctx->PSSetSamplers(0, 1, &state.sampler); + } + } + + // RS + { + // Set the Viewport + { + D3D11_VIEWPORT viewport = { + pass.viewport.x, pass.viewport.y, + pass.viewport.w, pass.viewport.h, + 0.0f, 1.0f }; + + ctx->RSSetViewports(1, &viewport); + } + + // Scissor Rect + if (pass.has_scissor) + { + D3D11_RECT scissor = { + pass.scissor.x, pass.scissor.y, + pass.scissor.x + pass.scissor.w, pass.scissor.y + pass.scissor.h }; + + ctx->RSSetScissorRects(1, &scissor); + } + else + { + ctx->RSSetScissorRects(0, nullptr); + } + + // Rasterizer + // TODO: Doesn't actually assign proper values + // TODO: Cache this + { + if (state.rasterizer) + state.rasterizer->Release(); + + D3D11_RASTERIZER_DESC rasterizerDesc = {}; + rasterizerDesc.FillMode = D3D11_FILL_SOLID; + rasterizerDesc.CullMode = D3D11_CULL_NONE; + rasterizerDesc.FrontCounterClockwise = true; + rasterizerDesc.DepthBias = 0; + rasterizerDesc.DepthBiasClamp = 0; + rasterizerDesc.SlopeScaledDepthBias = 0; + rasterizerDesc.DepthClipEnable = false; + rasterizerDesc.ScissorEnable = pass.has_scissor; + rasterizerDesc.MultisampleEnable = false; + rasterizerDesc.AntialiasedLineEnable = false; + + state.device->CreateRasterizerState(&rasterizerDesc, &state.rasterizer); + ctx->RSSetState(state.rasterizer); + } + } + + // Draw + { + if (mesh->instance_count() <= 0) + { + ctx->DrawIndexed(pass.index_count, pass.index_start, 0); + } + else + { + // TODO: + // Draw Instanced data + BLAH_ASSERT(false, "Instanced Drawing not implemented yet"); + } + } + + // UnBind Shader Resources + { + auto& textures = pass.material->textures(); + ID3D11ShaderResourceView* view = nullptr; + for (int i = 0; i < textures.size(); i++) + ctx->PSSetShaderResources(i, 1, &view); + } + } + + void GraphicsBackend::clear_backbuffer(Color color) + { + float clear[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; + state.context->ClearRenderTargetView(state.backbuffer, clear); + } + + ID3D11InputLayout* D3D11::get_layout(D3D11_Shader* shader, const VertexFormat& format) + { + // find existing + for (auto& it : layout_cache) + { + if (it.shader_hash == shader->hash && it.format.stride == format.stride && it.format.attributes.size() == format.attributes.size()) + { + bool same_format = true; + for (int n = 0; same_format && n < format.attributes.size(); n++) + if (it.format.attributes[n].index != format.attributes[n].index || + it.format.attributes[n].type != format.attributes[n].type || + it.format.attributes[n].normalized != format.attributes[n].normalized) + same_format = false; + + if (same_format) + return it.layout; + } + } + + // create a new one + Vector desc; + for (int i = 0; i < shader->attributes.size(); i++) + { + auto it = desc.expand(); + it->SemanticName = shader->attributes[i].semantic_name; + it->SemanticIndex = shader->attributes[i].semantic_index; + + if (!format.attributes[i].normalized) + { + switch (format.attributes[i].type) + { + case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break; + case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break; + case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break; + case VertexType::Float4: it->Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; + case VertexType::Byte4: it->Format = DXGI_FORMAT_R8G8B8A8_SINT; break; + case VertexType::UByte4: it->Format = DXGI_FORMAT_R8G8B8A8_UINT; break; + case VertexType::Short2: it->Format = DXGI_FORMAT_R16G16_SINT; break; + case VertexType::UShort2: it->Format = DXGI_FORMAT_R16G16_UINT; break; + case VertexType::Short4: it->Format = DXGI_FORMAT_R16G16B16A16_SINT; break; + case VertexType::UShort4: it->Format = DXGI_FORMAT_R16G16B16A16_UINT; break; + } + } + else + { + switch (format.attributes[i].type) + { + case VertexType::Float: it->Format = DXGI_FORMAT_R32_FLOAT; break; + case VertexType::Float2: it->Format = DXGI_FORMAT_R32G32_FLOAT; break; + case VertexType::Float3: it->Format = DXGI_FORMAT_R32G32B32_FLOAT; break; + case VertexType::Float4: it->Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; + case VertexType::Byte4: it->Format = DXGI_FORMAT_R8G8B8A8_SNORM; break; + case VertexType::UByte4: it->Format = DXGI_FORMAT_R8G8B8A8_UNORM; break; + case VertexType::Short2: it->Format = DXGI_FORMAT_R16G16_SNORM; break; + case VertexType::UShort2: it->Format = DXGI_FORMAT_R16G16_UNORM; break; + case VertexType::Short4: it->Format = DXGI_FORMAT_R16G16B16A16_SNORM; break; + case VertexType::UShort4: it->Format = DXGI_FORMAT_R16G16B16A16_UNORM; break; + } + } + + it->InputSlot = 0; + it->AlignedByteOffset = (i == 0 ? 0 : D3D11_APPEND_ALIGNED_ELEMENT); + it->InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + it->InstanceDataStepRate = 0; + } + + ID3D11InputLayout* layout = nullptr; + + auto hr = device->CreateInputLayout( + desc.begin(), + desc.size(), + shader->vertex_blob->GetBufferPointer(), + shader->vertex_blob->GetBufferSize(), + &layout); + + if (SUCCEEDED(hr)) + { + auto entry = layout_cache.expand(); + entry->shader_hash = shader->hash; + entry->format = format; + entry->layout = layout; + return layout; + } + + return nullptr; + } + + ID3D11BlendState* D3D11::get_blend(const BlendMode& blend) + { + for (auto& it : blend_cache) + if (it.blend == blend) + return it.state; + + D3D11_BLEND_DESC desc = { 0 }; + desc.AlphaToCoverageEnable = 0; + desc.IndependentBlendEnable = 0; + + desc.RenderTarget[0].BlendEnable = !( + blend.color_src == BlendFactor::One && blend.color_dst == BlendFactor::Zero && + blend.alpha_src == BlendFactor::One && blend.alpha_dst == BlendFactor::Zero + ); + + desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + + if (desc.RenderTarget[0].BlendEnable) + { + desc.RenderTarget[0].BlendOp = blend_op(blend.color_op); + desc.RenderTarget[0].SrcBlend = blend_factor(blend.color_src); + desc.RenderTarget[0].DestBlend = blend_factor(blend.color_dst); + + desc.RenderTarget[0].BlendOpAlpha = blend_op(blend.alpha_op); + desc.RenderTarget[0].SrcBlendAlpha = blend_factor(blend.alpha_src); + desc.RenderTarget[0].DestBlendAlpha = blend_factor(blend.alpha_dst); + } + + for (int i = 1; i < 8; i ++) + desc.RenderTarget[i] = desc.RenderTarget[0]; + + ID3D11BlendState* blend_state = nullptr; + auto hr = state.device->CreateBlendState(&desc, &blend_state); + + if (SUCCEEDED(hr)) + { + auto entry = blend_cache.expand(); + entry->blend = blend; + entry->state = blend_state; + return blend_state; + } + + return nullptr; + } +} + +#endif // BLAH_USE_D3D11 diff --git a/private/blah/internal/graphics_backend_gl.cpp b/private/blah/internal/graphics_backend_gl.cpp index ee0a920..c544d00 100644 --- a/private/blah/internal/graphics_backend_gl.cpp +++ b/private/blah/internal/graphics_backend_gl.cpp @@ -406,7 +406,7 @@ namespace Blah } // assign attributes - GLuint gl_mesh_assign_attributes(GLuint buffer, GLenum buffer_type, const VertexAttribute* vertex_attributes, int vertex_attribute_count, int stride, GLint divisor) + GLuint gl_mesh_assign_attributes(GLuint buffer, GLenum buffer_type, const VertexFormat& format, GLint divisor) { // bind gl.BindBuffer(buffer_type, buffer); @@ -417,49 +417,83 @@ namespace Blah // enable attributes size_t ptr = 0; - for (int n = 0; n < vertex_attribute_count; n++) + for (int n = 0; n < format.attributes.size(); n++) { - const VertexAttribute* attrib = (vertex_attributes + n); + auto& attribute = format.attributes[n]; + GLenum type = GL_UNSIGNED_BYTE; + size_t component_size = 0; + int components = 1; - for (int i = 0, loc = 0; i < (int)attrib->components; i += 4, loc++) + if (attribute.type == VertexType::Float) { - int components = attrib->components - i; - if (components > 4) - components = 4; - - GLenum type = GL_UNSIGNED_BYTE; - size_t component_size = 0; - if (attrib->type == VertexAttributeType::Byte) - { - type = GL_UNSIGNED_BYTE; - component_size = 1; - } - else if (attrib->type == VertexAttributeType::Short) - { - type = GL_SHORT; - component_size = 2; - } - else if (attrib->type == VertexAttributeType::Int) - { - type = GL_INT; - component_size = 4; - } - else if (attrib->type == VertexAttributeType::Float) - { - type = GL_FLOAT; - component_size = 4; - } - - uint32_t location = (uint32_t)(attrib->index + loc); - gl.EnableVertexAttribArray(location); - gl.VertexAttribPointer(location, components, type, attrib->normalized, stride, (void*)ptr); - gl.VertexAttribDivisor(location, divisor); - - ptr += components * component_size; + type = GL_FLOAT; + component_size = 4; + components = 1; } + else if (attribute.type == VertexType::Float2) + { + type = GL_FLOAT; + component_size = 4; + components = 2; + } + else if (attribute.type == VertexType::Float3) + { + type = GL_FLOAT; + component_size = 4; + components = 3; + } + else if (attribute.type == VertexType::Float4) + { + type = GL_FLOAT; + component_size = 4; + components = 4; + } + else if (attribute.type == VertexType::Byte4) + { + type = GL_BYTE; + component_size = 1; + components = 4; + } + else if (attribute.type == VertexType::UByte4) + { + type = GL_UNSIGNED_BYTE; + component_size = 1; + components = 4; + } + else if (attribute.type == VertexType::Short2) + { + type = GL_SHORT; + component_size = 2; + components = 2; + } + else if (attribute.type == VertexType::UShort2) + { + type = GL_UNSIGNED_SHORT; + component_size = 2; + components = 2; + } + else if (attribute.type == VertexType::Short4) + { + type = GL_SHORT; + component_size = 2; + components = 4; + } + else if (attribute.type == VertexType::UShort4) + { + type = GL_UNSIGNED_SHORT; + component_size = 2; + components = 4; + } + + uint32_t location = (uint32_t)(attribute.index); + gl.EnableVertexAttribArray(location); + gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr); + gl.VertexAttribDivisor(location, divisor); + + ptr += components * component_size; } - return stride; + return format.stride; } // convert blend op enum @@ -554,12 +588,6 @@ namespace Blah m_gl_format = GL_RG; m_gl_type = GL_UNSIGNED_BYTE; } - else if (format == TextureFormat::RGB) - { - m_gl_internal_format = GL_RGB; - m_gl_format = GL_RGB; - m_gl_type = GL_UNSIGNED_BYTE; - } else if (format == TextureFormat::RGBA) { m_gl_internal_format = GL_RGBA; @@ -779,7 +807,6 @@ namespace Blah { m_id = 0; - // vertex shader if (data->vertex == nullptr) { Log::error("Vertex Shader is required"); @@ -819,33 +846,32 @@ namespace Blah if (log_length > 0) { + gl.DeleteShader(vertex_shader); gl.DeleteShader(fragment_shader); Log::error(log); return; } } + // create actual shader program GLuint id = gl.CreateProgram(); gl.AttachShader(id, vertex_shader); gl.AttachShader(id, fragment_shader); gl.LinkProgram(id); gl.GetProgramInfoLog(id, 1024, &log_length, log); + gl.DetachShader(id, vertex_shader); + gl.DetachShader(id, fragment_shader); + gl.DeleteShader(vertex_shader); + gl.DeleteShader(fragment_shader); if (log_length > 0) { - gl.DetachShader(id, vertex_shader); - gl.DetachShader(id, fragment_shader); - gl.DeleteShader(vertex_shader); - gl.DeleteShader(fragment_shader); - Log::error(log); return; } - // ready to go - m_id = id; - // get uniforms + bool valid_uniforms = true; { const int max_name_length = 256; @@ -876,32 +902,48 @@ namespace Blah UniformInfo uniform; uniform.name = name; uniform.type = UniformType::None; + uniform.buffer_index = 0; uniform.array_length = size; uniform_locations.push_back(gl.GetUniformLocation(id, name)); - if (type == GL_FLOAT) - uniform.type = UniformType::Float; - else if (type == GL_FLOAT_VEC2) - uniform.type = UniformType::Float2; - else if (type == GL_FLOAT_VEC3) - uniform.type = UniformType::Float3; - else if (type == GL_FLOAT_VEC4) - uniform.type = UniformType::Float4; - else if (type == GL_FLOAT_MAT3x2) - uniform.type = UniformType::Mat3x2; - else if (type == GL_FLOAT_MAT4) - uniform.type = UniformType::Mat4x4; - else if (type == GL_SAMPLER_2D) + if (type == GL_SAMPLER_2D) + { uniform.type = UniformType::Texture; + uniform.shader = ShaderType::Fragment; + } else { - Log::error("Unsupported Uniform Type. Must be either FLOAT, MAT3x2, MAT4, or SAMPLER_2D"); - break; + uniform.shader = (ShaderType)((int)ShaderType::Vertex | (int)ShaderType::Fragment); + + if (type == GL_FLOAT) + uniform.type = UniformType::Float; + else if (type == GL_FLOAT_VEC2) + uniform.type = UniformType::Float2; + else if (type == GL_FLOAT_VEC3) + uniform.type = UniformType::Float3; + else if (type == GL_FLOAT_VEC4) + uniform.type = UniformType::Float4; + else if (type == GL_FLOAT_MAT3x2) + uniform.type = UniformType::Mat3x2; + else if (type == GL_FLOAT_MAT4) + uniform.type = UniformType::Mat4x4; + else + { + valid_uniforms = false; + Log::error("Unsupported Uniform Type"); + break; + } } m_uniforms.push_back(uniform); } } + + // assign ID if the uniforms were valid + if (!valid_uniforms) + gl.DeleteProgram(id); + else + m_id = id; } ~OpenGL_Shader() @@ -943,6 +985,8 @@ namespace Blah uint8_t m_instance_attribs_enabled; Vector m_vertex_attribs; Vector m_instance_attribs; + GLenum m_index_format; + int m_index_size; public: @@ -981,31 +1025,17 @@ namespace Blah return m_id; } - virtual void vertex_format_internal(const VertexAttribute* attributes, int attribute_count, int stride = -1) override + GLenum gl_index_format() const { - gl.BindVertexArray(m_id); - { - if (m_vertex_buffer == 0) - gl.GenBuffers(1, &(m_vertex_buffer)); - - m_vertex_size = gl_mesh_assign_attributes(m_vertex_buffer, GL_ARRAY_BUFFER, attributes, attribute_count, stride, 0); - } - gl.BindVertexArray(0); + return m_index_format; } - virtual void instance_format_internal(const VertexAttribute* attributes, int attribute_count, int stride = -1) override + int gl_index_size() const { - gl.BindVertexArray(m_id); - { - if (m_instance_buffer == 0) - gl.GenBuffers(1, &(m_instance_buffer)); - - m_instance_size = gl_mesh_assign_attributes(m_instance_buffer, GL_ARRAY_BUFFER, attributes, attribute_count, stride, 1); - } - gl.BindVertexArray(0); + return m_index_size; } - virtual void index_data(const void* indices, int64_t count) override + virtual void index_data(IndexFormat format, const void* indices, int64_t count) override { m_index_count = count; @@ -1014,48 +1044,64 @@ namespace Blah if (m_index_buffer == 0) gl.GenBuffers(1, &(m_index_buffer)); + switch (format) + { + case IndexFormat::UInt16: + m_index_format = GL_UNSIGNED_SHORT; + m_index_size = 2; + break; + case IndexFormat::UInt32: + m_index_format = GL_UNSIGNED_INT; + m_index_size = 4; + break; + } + gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); - gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * count, indices, GL_DYNAMIC_DRAW); + gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_size * count, indices, GL_DYNAMIC_DRAW); } gl.BindVertexArray(0); } - virtual void vertex_data(const void* vertices, int64_t count) override + virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) override { - if (m_vertex_buffer == 0 || m_vertex_size <= 0) - { - Log::error("You must assign a Vertex Format before setting Vertex Data"); - } - else - { - m_vertex_count = count; + m_vertex_count = count; - gl.BindVertexArray(m_id); - { - gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); - gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW); - } - gl.BindVertexArray(0); + gl.BindVertexArray(m_id); + { + // Create Buffer if it doesn't exist yet + if (m_vertex_buffer == 0) + gl.GenBuffers(1, &(m_vertex_buffer)); + + // TODO: + // Cache this + m_vertex_size = gl_mesh_assign_attributes(m_vertex_buffer, GL_ARRAY_BUFFER, format, 0); + + // Upload Buffer + gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); + gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW); } + gl.BindVertexArray(0); } - virtual void instance_data(const void* instances, int64_t count) override + virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) override { - if (m_instance_buffer == 0 || m_instance_size <= 0) - { - Log::error("You must assign an Instance Format before setting Instance Data"); - } - else - { - m_instance_count = count; + m_instance_count = count; - gl.BindVertexArray(m_id); - { - gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer); - gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW); - } - gl.BindVertexArray(0); + gl.BindVertexArray(m_id); + { + // Create Buffer if it doesn't exist yet + if (m_instance_buffer == 0) + gl.GenBuffers(1, &(m_instance_buffer)); + + // TODO: + // Cache this + m_instance_size = gl_mesh_assign_attributes(m_instance_buffer, GL_ARRAY_BUFFER, format, 1); + + // Upload Buffer + gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer); + gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW); } + gl.BindVertexArray(0); } virtual int64_t index_count() const override @@ -1222,6 +1268,7 @@ namespace Blah // Use the Shader // TODO: I don't love how material values are assigned or set here + // TODO: this should be cached? { gl.UseProgram(shader->gl_id()); @@ -1230,6 +1277,8 @@ namespace Blah GLint texture_ids[64]; auto& uniforms = shader->uniforms(); + auto data = pass.material->data(); + for (int i = 0; i < uniforms.size(); i++) { auto location = shader->uniform_locations[i]; @@ -1258,36 +1307,44 @@ namespace Blah } gl.Uniform1iv(location, (GLint)uniform.array_length, &texture_ids[0]); + continue; } + // Float - else if (uniform.type == UniformType::Float) + if (uniform.type == UniformType::Float) { - gl.Uniform1fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i)); + gl.Uniform1fv(location, (GLint)uniform.array_length, data); + data += uniform.array_length; } // Float2 else if (uniform.type == UniformType::Float2) { - gl.Uniform2fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i)); + gl.Uniform2fv(location, (GLint)uniform.array_length, data); + data += 2 * uniform.array_length; } // Float3 else if (uniform.type == UniformType::Float3) { - gl.Uniform3fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i)); + gl.Uniform3fv(location, (GLint)uniform.array_length, data); + data += 3 * uniform.array_length; } // Float4 else if (uniform.type == UniformType::Float4) { - gl.Uniform4fv(location, (GLint)uniform.array_length, (const GLfloat*)pass.material->get_value(i)); + gl.Uniform4fv(location, (GLint)uniform.array_length, data); + data += 4 * uniform.array_length; } // Matrix3x2 else if (uniform.type == UniformType::Mat3x2) { - gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, (const GLfloat*)pass.material->get_value(i)); + gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, data); + data += 6 * uniform.array_length; } // Matrix4x4 else if (uniform.type == UniformType::Mat4x4) { - gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, (const GLfloat*)pass.material->get_value(i)); + gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, data); + data += 16 * uniform.array_length; } } } @@ -1416,13 +1473,16 @@ namespace Blah { gl.BindVertexArray(mesh->gl_id()); + GLenum index_format = mesh->gl_index_format(); + int index_size = mesh->gl_index_size(); + if (pass.instance_count > 0) { gl.DrawElementsInstanced( GL_TRIANGLES, (GLint)(pass.index_count), - GL_UNSIGNED_INT, - (void*)(sizeof(int) * pass.index_start), + index_format, + (void*)(index_size* pass.index_start), (GLint)pass.instance_count); } else @@ -1430,8 +1490,8 @@ namespace Blah gl.DrawElements( GL_TRIANGLES, (GLint)(pass.index_count), - GL_UNSIGNED_INT, - (void*)(sizeof(int) * pass.index_start)); + index_format, + (void*)(index_size * pass.index_start)); } gl.BindVertexArray(0); diff --git a/private/blah/internal/platform_backend.h b/private/blah/internal/platform_backend.h index ebfe734..8918b5e 100644 --- a/private/blah/internal/platform_backend.h +++ b/private/blah/internal/platform_backend.h @@ -113,5 +113,8 @@ namespace Blah void* gl_context_create(); void gl_context_make_current(void* context); void gl_context_destroy(void* context); + + // D3D11 Methods + void* d3d11_get_hwnd(); } } \ No newline at end of file diff --git a/private/blah/internal/platform_backend_sdl2.cpp b/private/blah/internal/platform_backend_sdl2.cpp index 1dd827c..3085455 100644 --- a/private/blah/internal/platform_backend_sdl2.cpp +++ b/private/blah/internal/platform_backend_sdl2.cpp @@ -10,6 +10,7 @@ #include #include +#include #if _WIN32 // on Windows we're using the C++ API for now @@ -75,7 +76,7 @@ bool PlatformBackend::init(const Config* config) int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; - // GL Attributes + // enable OpenGL if (App::renderer() == Renderer::OpenGL) { flags |= SDL_WINDOW_OPENGL; @@ -93,6 +94,11 @@ bool PlatformBackend::init(const Config* config) SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); } + // enable DirectX + else if (App::renderer() == Renderer::D3D11) + { + + } // create the window window = SDL_CreateWindow(config->name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, config->width, config->height, flags); @@ -643,4 +649,12 @@ void PlatformBackend::gl_context_destroy(void* context) SDL_GL_DeleteContext(context); } +void* PlatformBackend::d3d11_get_hwnd() +{ + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + SDL_GetWindowWMInfo(window, &info); + return info.info.win.window; +} + #endif // BLAH_USE_SDL2 diff --git a/public/blah/containers/stackvector.h b/public/blah/containers/stackvector.h index ef7b918..0e44ca1 100644 --- a/public/blah/containers/stackvector.h +++ b/public/blah/containers/stackvector.h @@ -15,8 +15,10 @@ namespace Blah int m_count; public: + static inline constexpr size_t MaxCapacity = Capacity; StackVector(); + StackVector(const std::initializer_list& init); StackVector(const StackVector& src); StackVector(StackVector&& src) noexcept; ~StackVector(); @@ -59,6 +61,14 @@ namespace Blah m_count = 0; } + template + inline StackVector::StackVector(const std::initializer_list& init) + { + m_count = 0; + for (auto& it : init) + push_back(it); + } + template inline StackVector::StackVector(const StackVector& src) { diff --git a/public/blah/drawing/batch.cpp b/public/blah/drawing/batch.cpp index 1314f75..b21c95e 100644 --- a/public/blah/drawing/batch.cpp +++ b/public/blah/drawing/batch.cpp @@ -8,52 +8,121 @@ #include using namespace Blah; +namespace +{ -// TODO: -// This shader needs to be graphics API agnostic + // TODO: + // This shader needs to be graphics API agnostic -const ShaderData data = { - // vertex shader - "#version 330\n" - "uniform mat4 u_matrix;\n" - "layout(location=0) in vec2 a_position;\n" - "layout(location=1) in vec2 a_tex;\n" - "layout(location=2) in vec4 a_color;\n" - "layout(location=3) in vec3 a_type;\n" - "out vec2 v_tex;\n" - "out vec4 v_col;\n" - "out vec3 v_type;\n" - "void main(void)\n" - "{\n" - " gl_Position = u_matrix * vec4(a_position.xy, 0, 1);\n" - " v_tex = a_tex;\n" - " v_col = a_color;\n" - " v_type = a_type;\n" - "}", +#ifdef BLAH_USE_OPENGL - // fragment shader - "#version 330\n" - "uniform sampler2D u_texture;\n" - "in vec2 v_tex;\n" - "in vec4 v_col;\n" - "in vec3 v_type;\n" - "out vec4 o_color;\n" - "void main(void)\n" - "{\n" - " vec4 color = texture(u_texture, v_tex);\n" - " o_color = \n" - " v_type.x * color * v_col + \n" - " v_type.y * color.a * v_col + \n" - " v_type.z * v_col;\n" - "}" -}; + const ShaderData shader_data = { + // vertex shader + "#version 330\n" + "uniform mat4 u_matrix;\n" + "layout(location=0) in vec2 a_position;\n" + "layout(location=1) in vec2 a_tex;\n" + "layout(location=2) in vec4 a_color;\n" + "layout(location=3) in vec4 a_type;\n" + "out vec2 v_tex;\n" + "out vec4 v_col;\n" + "out vec4 v_type;\n" + "void main(void)\n" + "{\n" + " gl_Position = u_matrix * vec4(a_position.xy, 0, 1);\n" + " v_tex = a_tex;\n" + " v_col = a_color;\n" + " v_type = a_type;\n" + "}", -const VertexAttribute attributes[4] = { - { 0, VertexSemantics::Position, VertexAttributeType::Float, 2, false }, - { 1, VertexSemantics::Texcoord0, VertexAttributeType::Float, 2, false }, - { 2, VertexSemantics::Color0, VertexAttributeType::Byte, 4, true }, - { 3, VertexSemantics::Texcoord1, VertexAttributeType::Byte, 3, true }, -}; + // fragment shader + "#version 330\n" + "uniform sampler2D u_texture;\n" + "in vec2 v_tex;\n" + "in vec4 v_col;\n" + "in vec4 v_type;\n" + "out vec4 o_color;\n" + "void main(void)\n" + "{\n" + " vec4 color = texture(u_texture, v_tex);\n" + " o_color = \n" + " v_type.x * color * v_col + \n" + " v_type.y * color.a * v_col + \n" + " v_type.z * v_col;\n" + "}" + }; + +#elif BLAH_USE_D3D11 + + const char* d3d11_shader = "" + "cbuffer constants : register(b0)\n" + "{\n" + " row_major float4x4 u_matrix;\n" + "}\n" + + "struct vs_in\n" + "{\n" + " float2 position : POS;\n" + " float2 texcoord : TEX;\n" + " float4 color : COL;\n" + " float4 mask : MASK;\n" + "};\n" + + "struct vs_out\n" + "{\n" + " float4 position : SV_POSITION;\n" + " float2 texcoord : TEX;\n" + " float4 color : COL;\n" + " float4 mask : MASK;\n" + "};\n" + + "Texture2D u_texture : register(t0);\n" + "SamplerState u_sampler : register(s0);\n" + + "vs_out vs_main(vs_in input)\n" + "{\n" + " vs_out output;\n" + + " output.position = mul(float4(input.position, 0.0f, 1.0f), u_matrix);\n" + " output.texcoord = input.texcoord;\n" + " output.color = input.color;\n" + " output.mask = input.mask;\n" + + " return output;\n" + "}\n" + + "float4 ps_main(vs_out input) : SV_TARGET\n" + "{\n" + " float4 color = u_texture.Sample(u_sampler, input.texcoord);\n" + " return\n" + " input.mask.x * color * input.color + \n" + " input.mask.y * color.a * input.color + \n" + " input.mask.z * input.color;\n" + "}\n"; + + const ShaderData shader_data = { + d3d11_shader, + d3d11_shader, + { + { "POS", 0 }, + { "TEX", 0 }, + { "COL", 0 }, + { "MASK", 0 }, + } + }; + +#else + const ShaderData shader_data; +#endif + + const VertexFormat format = VertexFormat( + { + { 0, VertexType::Float2, false }, + { 1, VertexType::Float2, false }, + { 2, VertexType::UByte4, true }, + { 3, VertexType::UByte4, true }, + }); +} namespace { @@ -84,13 +153,13 @@ namespace #define PUSH_QUAD(px0, py0, px1, py1, px2, py2, px3, py3, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3, col0, col1, col2, col3, mult, fill, wash) \ { \ m_batch.elements += 2; \ - int* _i = m_indices.expand(6); \ - *_i++ = (int)m_vertices.size() + 0; \ - *_i++ = (int)m_vertices.size() + 1; \ - *_i++ = (int)m_vertices.size() + 2; \ - *_i++ = (int)m_vertices.size() + 0; \ - *_i++ = (int)m_vertices.size() + 2; \ - *_i++ = (int)m_vertices.size() + 3; \ + auto _i = m_indices.expand(6); \ + *_i++ = (uint32_t)m_vertices.size() + 0; \ + *_i++ = (uint32_t)m_vertices.size() + 1; \ + *_i++ = (uint32_t)m_vertices.size() + 2; \ + *_i++ = (uint32_t)m_vertices.size() + 0; \ + *_i++ = (uint32_t)m_vertices.size() + 2; \ + *_i++ = (uint32_t)m_vertices.size() + 3; \ Vertex* _v = m_vertices.expand(4); \ MAKE_VERTEX(_v, m_matrix, px0, py0, tx0, ty0, col0, mult, fill, wash); _v++; \ MAKE_VERTEX(_v, m_matrix, px1, py1, tx1, ty1, col1, mult, fill, wash); _v++; \ @@ -101,10 +170,10 @@ namespace #define PUSH_TRIANGLE(px0, py0, px1, py1, px2, py2, tx0, ty0, tx1, ty1, tx2, ty2, col0, col1, col2, mult, fill, wash) \ { \ m_batch.elements += 1; \ - int* _i = m_indices.expand(3); \ - *_i++ = (int)m_vertices.size() + 0; \ - *_i++ = (int)m_vertices.size() + 1; \ - *_i++ = (int)m_vertices.size() + 2; \ + auto* _i = m_indices.expand(3); \ + *_i++ = (uint32_t)m_vertices.size() + 0; \ + *_i++ = (uint32_t)m_vertices.size() + 1; \ + *_i++ = (uint32_t)m_vertices.size() + 2; \ Vertex* _v = m_vertices.expand(3); \ MAKE_VERTEX(_v, m_matrix, px0, py0, tx0, ty0, col0, mult, fill, wash); _v++; \ MAKE_VERTEX(_v, m_matrix, px1, py1, tx1, ty1, col1, mult, fill, wash); _v++; \ @@ -126,8 +195,6 @@ ShaderRef Batch::m_default_shader; Batch::Batch() { matrix_uniform = "u_matrix"; - texture_uniform = "u_texture"; - clear(); } @@ -292,21 +359,18 @@ void Batch::render(const FrameBufferRef& target, const Mat4x4& matrix) // define defaults { if (!m_mesh) - { m_mesh = Mesh::create(); - m_mesh->vertex_format(attributes, 4, sizeof(Vertex)); - } if (!m_default_shader) - m_default_shader = Shader::create(&data); + m_default_shader = Shader::create(&shader_data); if (!m_default_material) m_default_material = Material::create(m_default_shader); } // upload data - m_mesh->index_data(m_indices.data(), m_indices.size()); - m_mesh->vertex_data(m_vertices.data(), m_vertices.size()); + m_mesh->index_data(IndexFormat::UInt32, m_indices.data(), m_indices.size()); + m_mesh->vertex_data(format, m_vertices.data(), m_vertices.size()); RenderPass pass; pass.target = target; @@ -330,7 +394,7 @@ void Batch::render_single_batch(RenderPass& pass, const DrawBatch& b, const Mat4 if (!pass.material) pass.material = m_default_material; - pass.material->set_texture(texture_uniform, b.texture, 0); + pass.material->set_texture(0, b.texture, 0); pass.material->set_value(matrix_uniform, &matrix.m11, 16); pass.blend = b.blend; diff --git a/public/blah/drawing/batch.h b/public/blah/drawing/batch.h index c65fab9..00bb97e 100644 --- a/public/blah/drawing/batch.h +++ b/public/blah/drawing/batch.h @@ -45,9 +45,6 @@ namespace Blah // The name of the Matrix Uniform in the Shader const char* matrix_uniform; - // The name of the Texture Uniform in the Shader - const char* texture_uniform; - Batch(); Batch(const Batch& other) = delete; Batch& operator=(const Batch& other) = delete; @@ -186,6 +183,7 @@ namespace Blah uint8_t mult; uint8_t wash; uint8_t fill; + uint8_t pad; }; struct DrawBatch @@ -217,7 +215,7 @@ namespace Blah uint8_t m_tex_wash; DrawBatch m_batch; Vector m_vertices; - Vector m_indices; + Vector m_indices; Vector m_matrix_stack; Vector m_scissor_stack; Vector m_blend_stack; diff --git a/public/blah/graphics/material.cpp b/public/blah/graphics/material.cpp index c419bc6..62b1b8e 100644 --- a/public/blah/graphics/material.cpp +++ b/public/blah/graphics/material.cpp @@ -42,7 +42,6 @@ Material::Material(const ShaderRef& shader) m_shader = shader; auto& uniforms = shader->uniforms(); - Vector float_offsets; int float_size = 0; for (auto& uniform : uniforms) @@ -57,13 +56,10 @@ Material::Material(const ShaderRef& shader) continue; } - float_offsets.push_back(float_size); float_size += calc_uniform_size(uniform); } m_data.expand(float_size); - for (auto& it : float_offsets) - m_floats.push_back(m_data.begin() + it); } const ShaderRef Material::shader() const @@ -75,29 +71,50 @@ void Material::set_texture(const char* name, const TextureRef& texture, int inde { BLAH_ASSERT(m_shader, "Material Shader is invalid"); - if (m_textures.size() > 0) + int offset = 0; + for (auto& uniform : m_shader->uniforms()) { - int offset = 0; - for (auto& uniform : m_shader->uniforms()) + if (uniform.type != UniformType::Texture) + continue; + + if (strcmp(uniform.name, name) == 0) { - if (uniform.type != UniformType::Texture) - continue; - - if (strcmp(uniform.name, name) == 0) - { - m_textures[offset + index] = texture; - return; - } - - offset += uniform.array_length; - if (offset + index >= m_textures.size()) - break; + m_textures[offset + index] = texture; + return; } + + offset += uniform.array_length; + if (offset + index >= m_textures.size()) + break; } Log::warn("No Texture Uniform '%s' at index [%i] exists", name, index); } +void Material::set_texture(int slot, const TextureRef& texture, int index) +{ + BLAH_ASSERT(m_shader, "Material Shader is invalid"); + + int s = 0; + int offset = 0; + for (auto& uniform : m_shader->uniforms()) + { + if (uniform.type == UniformType::Texture) + { + if (s == slot) + { + if (index > uniform.array_length) + break; + + m_textures[offset + index] = texture; + break; + } + offset += uniform.array_length; + s++; + } + } +} + TextureRef Material::get_texture(const char* name, int index) const { BLAH_ASSERT(m_shader, "Material Shader is invalid"); @@ -124,14 +141,19 @@ TextureRef Material::get_texture(int slot, int index) const { BLAH_ASSERT(m_shader, "Material Shader is invalid"); - int offset = 0; int s = 0; + int offset = 0; for (auto& uniform : m_shader->uniforms()) { if (uniform.type == UniformType::Texture) { if (s == slot) + { + if (index > uniform.array_length) + break; + return m_textures[offset + index]; + } offset += uniform.array_length; if (offset + index >= m_textures.size()) @@ -151,6 +173,7 @@ void Material::set_value(const char* name, const float* value, int64_t length) BLAH_ASSERT(length >= 0, "Length must be >= 0"); int index = 0; + int offset = 0; for (auto& uniform : m_shader->uniforms()) { if (uniform.type == UniformType::Texture || uniform.type == UniformType::None) @@ -165,10 +188,11 @@ void Material::set_value(const char* name, const float* value, int64_t length) length = max; } - memcpy(m_floats[index], value, sizeof(float) * length); + memcpy(m_data.begin() + offset, value, sizeof(float) * length); return; } + offset += calc_uniform_size(uniform); index++; } @@ -180,6 +204,7 @@ const float* Material::get_value(const char* name, int64_t* length) const BLAH_ASSERT(m_shader, "Material Shader is invalid"); int index = 0; + int offset = 0; for (auto& uniform : m_shader->uniforms()) { if (uniform.type == UniformType::Texture || uniform.type == UniformType::None) @@ -189,10 +214,11 @@ const float* Material::get_value(const char* name, int64_t* length) const { if (length != nullptr) *length = calc_uniform_size(uniform); - return m_floats[index]; + return m_data.begin() + offset; } index++; + offset += calc_uniform_size(uniform); } *length = 0; @@ -200,29 +226,12 @@ const float* Material::get_value(const char* name, int64_t* length) const Log::warn("No Uniform '%s' exists", name); } -const float* Material::get_value(int slot, int64_t* length) const +const Vector& Material::textures() const { - BLAH_ASSERT(m_shader, "Material Shader is invalid"); + return m_textures; +} - int index = 0; - int s = 0; - for (auto& uniform : m_shader->uniforms()) - { - if (uniform.type != UniformType::Texture && uniform.type != UniformType::None) - { - if (index == slot) - { - if (length != nullptr) - *length = calc_uniform_size(uniform); - return m_floats[index]; - } - index++; - } - - s++; - } - - Log::warn("No Uniform [%i] exists", slot); - *length = 0; - return nullptr; -} \ No newline at end of file +const float* Material::data() const +{ + return m_data.begin(); +} diff --git a/public/blah/graphics/material.h b/public/blah/graphics/material.h index 517e22e..f0dfcd9 100644 --- a/public/blah/graphics/material.h +++ b/public/blah/graphics/material.h @@ -31,38 +31,33 @@ namespace Blah // Returns the Shader assigned to the Material. const ShaderRef shader() const; - // Sets the texture. If the index is out of bounds, the Material is not valid, the Uniform - // is not a Texture Uniform, or the Uniform does not exist, this will do nothing. - void set_texture(const char* name, const TextureRef& texture, int index = 0); + // Sets the texture + void set_texture(const char* name, const TextureRef& texture, int array_index = 0); - // Gets the texture. - // If the Uniform does not exist, or the index is out of bounds, this will return - // an invalid Texture reference. - TextureRef get_texture(const char* name, int index = 0) const; + // Sets the texture + void set_texture(int slot, const TextureRef& texture, int array_index = 0); - // Gets the texture from the given slot. - // If the slot is not a Texture Uniform, or the index is out of bounds, this will return - // an invalid Texture reference. - TextureRef get_texture(int slot, int index = 0) const; + // Gets the texture, or an empty reference if invalid + TextureRef get_texture(const char* name, int array_index = 0) const; - // Sets the value. Length is the total amount of values to set. For example, if the Uniform type - // is a float2, and there are 4 elements, the maximum length should be 8. + // Gets the texture, or an empty reference if invalid + TextureRef get_texture(int slot, int array_index = 0) const; + + // Sets the value. `length` is the total number of floats to set void set_value(const char* name, const float* value, int64_t length); // Gets a pointer to the values of the given Uniform, or nullptr if it doesn't exist. - // Length is the total amount of values. For example, if the Uniform type - // is a float2, and there are 4 elements, the length should be 8. const float* get_value(const char* name, int64_t* length = nullptr) const; - // Gets a pointer to the values of the given Uniform, or nullptr if it doesn't exist. - // Length is the total amount of values. For example, if the Uniform type - // is a float2, and there are 4 elements, the length should be 8. - const float* get_value(int slot, int64_t* length = nullptr) const; + // Returns the internal Texture buffer + const Vector& textures() const; + + // Returns the interal float buffer of all the values + const float* data() const; private: ShaderRef m_shader; Vector m_textures; - Vector m_floats; Vector m_data; }; } \ No newline at end of file diff --git a/public/blah/graphics/mesh.cpp b/public/blah/graphics/mesh.cpp index bc18525..efc4ae9 100644 --- a/public/blah/graphics/mesh.cpp +++ b/public/blah/graphics/mesh.cpp @@ -3,55 +3,38 @@ using namespace Blah; + MeshRef Mesh::create() { return GraphicsBackend::create_mesh(); } -void Mesh::vertex_format(const VertexAttribute* attributes, int attribute_count, int stride) +VertexFormat::VertexFormat(std::initializer_list attributes, int stride) { - if (stride < 0) + for (auto& it : attributes) + this->attributes.push_back(it); + + if (stride <= 0) { stride = 0; - for (int n = 0; n < attribute_count; n++) + for (auto& it : attributes) { - const VertexAttribute* attrib = (attributes + n); - - if (attrib->type == VertexAttributeType::Byte) - stride += attrib->components * 1; - else if (attrib->type == VertexAttributeType::Short) - stride += attrib->components * 2; - else if (attrib->type == VertexAttributeType::Int) - stride += attrib->components * 4; - else if (attrib->type == VertexAttributeType::Float) - stride += attrib->components * 4; + switch (it.type) + { + case VertexType::Float: stride += 4; break; + case VertexType::Float2: stride += 8; break; + case VertexType::Float3: stride += 12; break; + case VertexType::Float4: stride += 16; break; + case VertexType::Byte4: stride += 4; break; + case VertexType::UByte4: stride += 4; break; + case VertexType::Short2: stride += 4; break; + case VertexType::UShort2: stride += 4; break; + case VertexType::Short4: stride += 8; break; + case VertexType::UShort4: stride += 8; break; + } } } - vertex_format_internal(attributes, attribute_count, stride); -} - -void Mesh::instance_format(const VertexAttribute* attributes, int attribute_count, int stride) -{ - if (stride < 0) - { - stride = 0; - - for (int n = 0; n < attribute_count; n++) - { - const VertexAttribute* attrib = (attributes + n); - - if (attrib->type == VertexAttributeType::Byte) - stride += attrib->components * 1; - else if (attrib->type == VertexAttributeType::Short) - stride += attrib->components * 2; - else if (attrib->type == VertexAttributeType::Int) - stride += attrib->components * 4; - else if (attrib->type == VertexAttributeType::Float) - stride += attrib->components * 4; - } - } - - instance_format_internal(attributes, attribute_count, stride); -} + this->stride = stride; +} \ No newline at end of file diff --git a/public/blah/graphics/mesh.h b/public/blah/graphics/mesh.h index 68840fd..343d5f5 100644 --- a/public/blah/graphics/mesh.h +++ b/public/blah/graphics/mesh.h @@ -1,47 +1,53 @@ #pragma once #include #include +#include namespace Blah { - enum class VertexSemantics + enum class VertexType { None, - Position, - Normal, - Bitangent, - Color0, - Color1, - Color2, - Color3, - Indices, - Weight, - Texcoord0, - Texcoord1, - Texcoord2, - Texcoord3, - Texcoord4, - Texcoord5, - Texcoord6, - Texcoord7 - }; - - enum class VertexAttributeType - { - None, - Byte, - Short, - Int, - Float + Float, + Float2, + Float3, + Float4, + Byte4, + UByte4, + Short2, + UShort2, + Short4, + UShort4 }; struct VertexAttribute { - int index; - VertexSemantics semantics; - VertexAttributeType type; - int components; - bool normalized; + // Location / Attribute Index + int index = 0; + + // Vertex Type + VertexType type = VertexType::None; + + // Whether the Vertex should be normalized (doesn't apply to Floats) + bool normalized = false; + }; + + struct VertexFormat + { + // List of Attributes + StackVector attributes; + + // Total size in bytes of each Vertex element + int stride = 0; + + VertexFormat() = default; + VertexFormat(std::initializer_list attributes, int stride = 0); + }; + + enum class IndexFormat + { + UInt16, + UInt32 }; class Mesh; @@ -66,22 +72,14 @@ namespace Blah // If the Mesh creation fails, it will return an invalid Mesh. static MeshRef create(); - // Sets the Vertex Format of the Mesh - void vertex_format(const VertexAttribute* attributes, int attribute_count, int stride = -1); - - // Sets the Instance Format of the Mesh - void instance_format(const VertexAttribute* attributes, int attribute_count, int stride = -1); - // Uploads the given index buffer to the Mesh - virtual void index_data(const void* indices, int64_t count) = 0; + virtual void index_data(IndexFormat format, const void* indices, int64_t count) = 0; // Uploads the given vertex buffer to the Mesh - // Note you must call vertex_format at least once before uploading vertices. - virtual void vertex_data(const void* vertices, int64_t count) = 0; + virtual void vertex_data(const VertexFormat& format, const void* vertices, int64_t count) = 0; // Uploads the given instance buffer to the Mesh - // Note you must call instance_format at least once before uploading instances. - virtual void instance_data(const void* instances, int64_t count) = 0; + virtual void instance_data(const VertexFormat& format, const void* instances, int64_t count) = 0; // Gets the index count of the Mesh virtual int64_t index_count() const = 0; @@ -91,9 +89,5 @@ namespace Blah // Gets the instance count of the Mesh virtual int64_t instance_count() const = 0; - - protected: - virtual void vertex_format_internal(const VertexAttribute* attributes, int count, int stride) = 0; - virtual void instance_format_internal(const VertexAttribute* attributes, int count, int stride) = 0; }; } \ No newline at end of file diff --git a/public/blah/graphics/shader.h b/public/blah/graphics/shader.h index 4ce355a..968076f 100644 --- a/public/blah/graphics/shader.h +++ b/public/blah/graphics/shader.h @@ -5,6 +5,7 @@ namespace Blah { + // Supported Uniform Types enum class UniformType { None, @@ -17,17 +18,54 @@ namespace Blah Texture }; + // Supported Shader Types + enum class ShaderType + { + None = 0, + Vertex = 1 << 0, + Fragment = 1 << 1 + }; + + // Uniform Info, provided by the Shader struct UniformInfo { + // Name of the Uniform String name; + + // The Value type of the Uniform UniformType type; + + // The Shader type the Uniform is a part of + ShaderType shader; + + // Some rendering APIs have uniform buffers. The `buffer_index` + // specifies which buffer the uniform belongs to + int buffer_index; + + // Array length of the Uniform (ex. a vec2[4] would be 4) int array_length; }; + // Data to be passed to the shader to construct it struct ShaderData { + struct HLSL_Attribute + { + // Semantic Name + const char* semantic_name = nullptr; + + // (optional) Semantic Index + int semantic_index = 0; + }; + + // Vertex Shader Program data const char* vertex; + + // Fragment Shader Program data const char* fragment; + + // HLSL Attributes - required for D3D11 + StackVector hlsl_attributes; }; class Shader; diff --git a/public/blah/graphics/texture.cpp b/public/blah/graphics/texture.cpp index 9b59430..ef18245 100644 --- a/public/blah/graphics/texture.cpp +++ b/public/blah/graphics/texture.cpp @@ -9,14 +9,16 @@ using namespace Blah; TextureRef Texture::create(const Image& image) { auto tex = create(image.width, image.height, TextureFormat::RGBA); - tex->set_data((unsigned char*)image.pixels); + if (tex) + tex->set_data((unsigned char*)image.pixels); return tex; } TextureRef Texture::create(int width, int height, unsigned char* rgba) { auto tex = create(width, height, TextureFormat::RGBA); - tex->set_data(rgba); + if (tex) + tex->set_data(rgba); return tex; } @@ -35,10 +37,11 @@ TextureRef Texture::create(Stream& stream) { Image img = Image(stream); - if (img.pixels != nullptr && img.width > 0 && img.height > 0) + if (img.pixels && img.width > 0 && img.height > 0) { auto tex = create(img.width, img.height, TextureFormat::RGBA); - tex->set_data((unsigned char*)img.pixels); + if (tex) + tex->set_data((unsigned char*)img.pixels); return tex; } @@ -49,10 +52,11 @@ TextureRef Texture::create(const char* file) { Image img = Image(file); - if (img.pixels != nullptr) + if (img.pixels) { auto tex = create(img.width, img.height, TextureFormat::RGBA); - tex->set_data((unsigned char*)img.pixels); + if (tex) + tex->set_data((unsigned char*)img.pixels); return tex; } diff --git a/public/blah/graphics/texture.h b/public/blah/graphics/texture.h index f0b6517..ac13190 100644 --- a/public/blah/graphics/texture.h +++ b/public/blah/graphics/texture.h @@ -22,7 +22,6 @@ namespace Blah None, R, RG, - RGB, RGBA, DepthStencil, Count