mirror of
https://github.com/NoelFB/blah.git
synced 2025-04-06 00:26:05 +08:00
1) Over time the total amount of files has decreased, and so it made sense to just simplify the folder structure and remove all of the subfolders. 2) Refactor the String class to utilize the existing Vector and StackVector classes instead of managing everything itself.
1660 lines
43 KiB
C++
1660 lines
43 KiB
C++
#ifdef BLAH_RENDERER_D3D11
|
|
|
|
// TODO:
|
|
// Note the D3D11 Implementation is still a work-in-progress
|
|
|
|
#include "blah_renderer.h"
|
|
#include "blah_internal.h"
|
|
#include "blah_platform.h"
|
|
#include <blah_common.h>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <cstddef>
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <d3d11.h>
|
|
#include <d3dcompiler.h>
|
|
|
|
// shorthand to our internal state
|
|
#define renderer ((Renderer_D3D11*)App::Internal::renderer)
|
|
|
|
namespace Blah
|
|
{
|
|
const char* d3d11_batch_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_texture_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_texture_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 d3d11_batch_shader_data = {
|
|
d3d11_batch_shader,
|
|
d3d11_batch_shader,
|
|
{
|
|
{ "POS", 0 },
|
|
{ "TEX", 0 },
|
|
{ "COL", 0 },
|
|
{ "MASK", 0 },
|
|
}
|
|
};
|
|
|
|
class D3D11_Shader;
|
|
|
|
class Renderer_D3D11 : public Renderer
|
|
{
|
|
public:
|
|
// main resources
|
|
ID3D11Device* device = nullptr;
|
|
ID3D11DeviceContext* context = nullptr;
|
|
IDXGISwapChain* swap_chain = nullptr;
|
|
ID3D11RenderTargetView* backbuffer_view = nullptr;
|
|
ID3D11DepthStencilView* backbuffer_depth_view = nullptr;
|
|
|
|
// last backbuffer size
|
|
Point drawable_size;
|
|
Point last_window_size;
|
|
|
|
struct StoredInputLayout
|
|
{
|
|
u32 shader_hash;
|
|
VertexFormat format;
|
|
ID3D11InputLayout* layout;
|
|
};
|
|
|
|
struct StoredBlendState
|
|
{
|
|
BlendMode blend;
|
|
ID3D11BlendState* state;
|
|
};
|
|
|
|
struct StoredRasterizer
|
|
{
|
|
Cull cull;
|
|
bool has_scissor;
|
|
ID3D11RasterizerState* state;
|
|
};
|
|
|
|
struct StoredSampler
|
|
{
|
|
TextureSampler sampler;
|
|
ID3D11SamplerState* state;
|
|
};
|
|
|
|
struct StoredDepthStencil
|
|
{
|
|
Compare depth;
|
|
ID3D11DepthStencilState* state;
|
|
};
|
|
|
|
Vector<StoredInputLayout> layout_cache;
|
|
Vector<StoredBlendState> blend_cache;
|
|
Vector<StoredRasterizer> rasterizer_cache;
|
|
Vector<StoredSampler> sampler_cache;
|
|
Vector<StoredDepthStencil> depthstencil_cache;
|
|
|
|
bool init() override;
|
|
void shutdown() override;
|
|
void update() override;
|
|
void before_render() override;
|
|
void after_render() override;
|
|
bool get_draw_size(int* w, int* h) override;
|
|
void render(const DrawCall& pass) override;
|
|
void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) override;
|
|
TextureRef create_texture(int width, int height, TextureFormat format) override;
|
|
TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) override;
|
|
ShaderRef create_shader(const ShaderData* data) override;
|
|
MeshRef create_mesh() override;
|
|
|
|
ID3D11InputLayout* get_layout(D3D11_Shader* shader, const VertexFormat& format);
|
|
ID3D11BlendState* get_blend(const BlendMode& blend);
|
|
ID3D11RasterizerState* get_rasterizer(const DrawCall& pass);
|
|
ID3D11SamplerState* get_sampler(const TextureSampler& sampler);
|
|
ID3D11DepthStencilState* get_depthstencil(const DrawCall& pass);
|
|
};
|
|
|
|
// Utility Methods
|
|
D3D11_BLEND_OP blend_op(BlendOp op);
|
|
D3D11_BLEND blend_factor(BlendFactor factor);
|
|
bool reflect_uniforms(Vector<UniformInfo>& append_uniforms_to, Vector<ID3D11Buffer*>& append_buffers_to, ID3DBlob* shader, ShaderType shader_type);
|
|
void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type);
|
|
|
|
class D3D11_Texture : public Texture
|
|
{
|
|
private:
|
|
int m_width;
|
|
int m_height;
|
|
TextureFormat m_format;
|
|
DXGI_FORMAT m_dxgi_format;
|
|
bool m_is_framebuffer;
|
|
int m_size;
|
|
|
|
public:
|
|
ID3D11Texture2D* texture = nullptr;
|
|
ID3D11Texture2D* staging = nullptr;
|
|
ID3D11ShaderResourceView* view = nullptr;
|
|
|
|
D3D11_Texture(int width, int height, TextureFormat format, bool is_framebuffer)
|
|
{
|
|
m_width = width;
|
|
m_height = height;
|
|
m_format = format;
|
|
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.CPUAccessFlags = 0;
|
|
desc.MiscFlags = 0;
|
|
|
|
bool is_depth_stencil = false;
|
|
|
|
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;
|
|
is_depth_stencil = true;
|
|
break;
|
|
case TextureFormat::None:
|
|
case TextureFormat::Count:
|
|
break;
|
|
}
|
|
|
|
if (!is_depth_stencil)
|
|
desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
|
|
else
|
|
desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
|
|
|
|
if (is_framebuffer && !is_depth_stencil)
|
|
desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
|
|
|
|
m_dxgi_format = desc.Format;
|
|
|
|
auto hr = renderer->device->CreateTexture2D(&desc, NULL, &texture);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
if (texture)
|
|
texture->Release();
|
|
texture = nullptr;
|
|
return;
|
|
}
|
|
|
|
if (!is_depth_stencil)
|
|
{
|
|
hr = renderer->device->CreateShaderResourceView(texture, NULL, &view);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
texture->Release();
|
|
texture = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
~D3D11_Texture()
|
|
{
|
|
if (texture)
|
|
texture->Release();
|
|
texture = nullptr;
|
|
if (staging)
|
|
staging->Release();
|
|
staging = nullptr;
|
|
if (view)
|
|
view->Release();
|
|
view = nullptr;
|
|
}
|
|
|
|
int width() const override
|
|
{
|
|
return m_width;
|
|
}
|
|
|
|
int height() const override
|
|
{
|
|
return m_height;
|
|
}
|
|
|
|
TextureFormat format() const override
|
|
{
|
|
return m_format;
|
|
}
|
|
|
|
void set_data(const u8* data) override
|
|
{
|
|
// bounds
|
|
D3D11_BOX box;
|
|
box.left = 0;
|
|
box.right = m_width;
|
|
box.top = 0;
|
|
box.bottom = m_height;
|
|
box.front = 0;
|
|
box.back = 1;
|
|
|
|
// set data
|
|
renderer->context->UpdateSubresource(
|
|
texture,
|
|
0,
|
|
&box,
|
|
data,
|
|
m_size / m_height,
|
|
0);
|
|
}
|
|
|
|
void get_data(u8* data) override
|
|
{
|
|
HRESULT hr;
|
|
|
|
// create staging texture
|
|
if (!staging)
|
|
{
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
desc.Width = m_width;
|
|
desc.Height = m_height;
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = m_dxgi_format;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.SampleDesc.Quality = 0;
|
|
desc.Usage = D3D11_USAGE_STAGING;
|
|
desc.BindFlags = 0;
|
|
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
|
desc.MiscFlags = 0;
|
|
|
|
hr = renderer->device->CreateTexture2D(&desc, NULL, &staging);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
BLAH_ASSERT(false, "Failed to create staging texture to get data");
|
|
return;
|
|
}
|
|
}
|
|
|
|
D3D11_BOX box;
|
|
box.left = 0;
|
|
box.right = m_width;
|
|
box.bottom = m_height;
|
|
box.top = 0;
|
|
box.front = 0;
|
|
box.back = 1;
|
|
|
|
// copy data to staging texture
|
|
renderer->context->CopySubresourceRegion(
|
|
staging, 0,
|
|
0, 0, 0,
|
|
texture, 0,
|
|
&box);
|
|
|
|
// get data
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
hr = renderer->context->Map(staging, 0, D3D11_MAP_READ, 0, &map);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
BLAH_ASSERT(false, "Failed to get texture data");
|
|
return;
|
|
}
|
|
|
|
int bytes_per_pixel = m_size / (m_width * m_height);
|
|
int bytes_per_row = m_width * bytes_per_pixel;
|
|
for (int y = 0; y < m_height; y++)
|
|
memcpy(data + y * bytes_per_row, (unsigned char*)map.pData + map.RowPitch * y, bytes_per_row);
|
|
|
|
renderer->context->Unmap(staging, 0);
|
|
}
|
|
|
|
bool is_framebuffer() const override
|
|
{
|
|
return m_is_framebuffer;
|
|
}
|
|
|
|
};
|
|
|
|
class D3D11_Target : public Target
|
|
{
|
|
private:
|
|
Attachments m_attachments;
|
|
|
|
public:
|
|
StackVector<ID3D11RenderTargetView*, Attachments::capacity - 1> color_views;
|
|
ID3D11DepthStencilView* depth_view = nullptr;
|
|
|
|
D3D11_Target(int width, int height, const TextureFormat* attachments, int attachment_count)
|
|
{
|
|
for (int i = 0; i < attachment_count; i++)
|
|
{
|
|
auto tex = new D3D11_Texture(width, height, attachments[i], true);
|
|
|
|
m_attachments.push_back(TextureRef(tex));
|
|
|
|
if (attachments[i] == TextureFormat::DepthStencil)
|
|
{
|
|
renderer->device->CreateDepthStencilView(tex->texture, nullptr, &depth_view);
|
|
}
|
|
else
|
|
{
|
|
ID3D11RenderTargetView* view = nullptr;
|
|
renderer->device->CreateRenderTargetView(tex->texture, nullptr, &view);
|
|
color_views.push_back(view);
|
|
}
|
|
}
|
|
}
|
|
|
|
~D3D11_Target()
|
|
{
|
|
for (auto& it : color_views)
|
|
it->Release();
|
|
color_views.clear();
|
|
|
|
if (depth_view)
|
|
depth_view->Release();
|
|
depth_view = nullptr;
|
|
}
|
|
|
|
Attachments& textures() override
|
|
{
|
|
return m_attachments;
|
|
}
|
|
|
|
const Attachments& textures() const override
|
|
{
|
|
return m_attachments;
|
|
}
|
|
|
|
void clear(Color color, float depth, u8 stencil, ClearMask mask) override
|
|
{
|
|
float col[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f };
|
|
|
|
if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
|
|
{
|
|
for (int i = 0; i < color_views.size(); i++)
|
|
renderer->context->ClearRenderTargetView(color_views[i], col);
|
|
}
|
|
|
|
if (depth_view)
|
|
{
|
|
UINT flags = 0;
|
|
if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth)
|
|
flags |= D3D11_CLEAR_DEPTH;
|
|
if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil)
|
|
flags |= D3D11_CLEAR_STENCIL;
|
|
|
|
if (flags != 0)
|
|
renderer->context->ClearDepthStencilView(depth_view, flags, depth, stencil);
|
|
}
|
|
}
|
|
};
|
|
|
|
class D3D11_Shader : public Shader
|
|
{
|
|
public:
|
|
ID3D11VertexShader* vertex = nullptr;
|
|
ID3D11PixelShader* fragment = nullptr;
|
|
ID3DBlob* vertex_blob = nullptr;
|
|
ID3DBlob* fragment_blob = nullptr;
|
|
Vector<ID3D11Buffer*> vertex_uniform_buffers;
|
|
Vector<ID3D11Buffer*> fragment_uniform_buffers;
|
|
Vector<Vector<float>> vertex_uniform_values;
|
|
Vector<Vector<float>> fragment_uniform_values;
|
|
StackVector<ShaderData::HLSL_Attribute, 16> attributes;
|
|
Vector<UniformInfo> uniform_list;
|
|
u32 hash = 0;
|
|
bool valid = false;
|
|
|
|
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.cstr(),
|
|
data->vertex.length(),
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
"vs_main",
|
|
"vs_5_0",
|
|
flags,
|
|
0,
|
|
&vertex_blob,
|
|
&error_blob);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Log::error("%s", (char*)error_blob->GetBufferPointer());
|
|
error_blob->Release();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// compile fragment shader
|
|
{
|
|
hr = D3DCompile(
|
|
data->fragment.cstr(),
|
|
data->fragment.length(),
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
"ps_main",
|
|
"ps_5_0",
|
|
flags,
|
|
0,
|
|
&fragment_blob,
|
|
&error_blob);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
Log::error("%s", (char*)error_blob->GetBufferPointer());
|
|
error_blob->Release();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// create vertex shader
|
|
{
|
|
hr = renderer->device->CreateVertexShader(
|
|
vertex_blob->GetBufferPointer(),
|
|
vertex_blob->GetBufferSize(),
|
|
NULL,
|
|
&vertex);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return;
|
|
}
|
|
|
|
// create fragment shader
|
|
{
|
|
hr = renderer->device->CreatePixelShader(
|
|
fragment_blob->GetBufferPointer(),
|
|
fragment_blob->GetBufferSize(),
|
|
NULL,
|
|
&fragment);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return;
|
|
}
|
|
|
|
// get uniforms
|
|
reflect_uniforms(uniform_list, vertex_uniform_buffers, vertex_blob, ShaderType::Vertex);
|
|
reflect_uniforms(uniform_list, fragment_uniform_buffers, 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--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// create CPU uniform buffers, so we don't need to create them during rendering
|
|
vertex_uniform_values.expand(vertex_uniform_buffers.size());
|
|
fragment_uniform_values.expand(fragment_uniform_buffers.size());
|
|
|
|
// copy HLSL attributes
|
|
attributes = data->hlsl_attributes;
|
|
|
|
// store hash
|
|
hash = 5381;
|
|
for (auto& it : attributes)
|
|
{
|
|
for (size_t i = 0, n = strlen(it.semantic_name); i < n; i++)
|
|
hash = ((hash << 5) + hash) + it.semantic_name[i];
|
|
hash = (it.semantic_index << 5) + hash;
|
|
}
|
|
|
|
// Shader is ready for use!
|
|
valid = true;
|
|
}
|
|
|
|
~D3D11_Shader() override
|
|
{
|
|
if (vertex)
|
|
vertex->Release();
|
|
vertex = nullptr;
|
|
|
|
if (vertex_blob)
|
|
vertex_blob->Release();
|
|
vertex_blob = nullptr;
|
|
|
|
if (fragment)
|
|
fragment->Release();
|
|
fragment = nullptr;
|
|
|
|
if (fragment_blob)
|
|
fragment_blob->Release();
|
|
fragment_blob = nullptr;
|
|
|
|
for (auto& it : vertex_uniform_buffers)
|
|
it->Release();
|
|
vertex_uniform_buffers.clear();
|
|
for (auto& it : fragment_uniform_buffers)
|
|
it->Release();
|
|
fragment_uniform_buffers.clear();
|
|
}
|
|
|
|
Vector<UniformInfo>& uniforms() override
|
|
{
|
|
return uniform_list;
|
|
}
|
|
|
|
const Vector<UniformInfo>& uniforms() const override
|
|
{
|
|
return uniform_list;
|
|
}
|
|
};
|
|
|
|
class D3D11_Mesh : public Mesh
|
|
{
|
|
private:
|
|
i64 m_vertex_count = 0;
|
|
i64 m_vertex_capacity = 0;
|
|
i64 m_index_count = 0;
|
|
i64 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();
|
|
vertex_buffer = nullptr;
|
|
if (index_buffer)
|
|
index_buffer->Release();
|
|
index_buffer = nullptr;
|
|
}
|
|
|
|
void index_data(IndexFormat format, const void* indices, i64 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(i16); break;
|
|
case IndexFormat::UInt32: index_stride = sizeof(i32); 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 = (UINT)(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
|
|
auto hr = renderer->device->CreateBuffer(&desc, &data, &index_buffer);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data");
|
|
}
|
|
}
|
|
else if (indices)
|
|
{
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
|
|
auto hr = renderer->context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data");
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
memcpy(map.pData, indices, index_stride * count);
|
|
renderer->context->Unmap(index_buffer, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void vertex_data(const VertexFormat& format, const void* vertices, i64 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 = (UINT)(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
|
|
auto hr = renderer->device->CreateBuffer(&desc, &data, &vertex_buffer);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data");
|
|
}
|
|
}
|
|
// otherwise just update it
|
|
else if (vertices)
|
|
{
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
auto hr = renderer->context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data");
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
memcpy(map.pData, vertices, vertex_format.stride * count);
|
|
renderer->context->Unmap(vertex_buffer, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void instance_data(const VertexFormat& format, const void* instances, i64 count) override
|
|
{
|
|
|
|
}
|
|
|
|
i64 index_count() const override
|
|
{
|
|
return m_index_count;
|
|
}
|
|
|
|
i64 vertex_count() const override
|
|
{
|
|
return m_vertex_count;
|
|
}
|
|
|
|
i64 instance_count() const override
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
bool Renderer_D3D11::init()
|
|
{
|
|
last_window_size = App::get_size();
|
|
|
|
// Define Swap Chain
|
|
DXGI_SWAP_CHAIN_DESC desc = {};
|
|
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 = 1;
|
|
desc.OutputWindow = (HWND)App::Internal::platform->d3d11_get_hwnd();
|
|
desc.Windowed = true;
|
|
|
|
// Creation Flags
|
|
UINT flags = D3D11_CREATE_DEVICE_SINGLETHREADED;
|
|
#if defined(DEBUG) || defined(_DEBUG)
|
|
flags |= D3D11_CREATE_DEVICE_DEBUG;
|
|
#endif
|
|
|
|
// Create D3D device & context & swap cahin
|
|
D3D_FEATURE_LEVEL feature_level;
|
|
HRESULT hr = D3D11CreateDeviceAndSwapChain(
|
|
NULL,
|
|
D3D_DRIVER_TYPE_HARDWARE,
|
|
NULL,
|
|
flags,
|
|
NULL,
|
|
0,
|
|
D3D11_SDK_VERSION,
|
|
&desc,
|
|
&swap_chain,
|
|
&device,
|
|
&feature_level,
|
|
&context);
|
|
|
|
// Exit out if it's not OK
|
|
if (!SUCCEEDED(hr) || !swap_chain || !device || !context)
|
|
return false;
|
|
|
|
// Get the backbuffer
|
|
ID3D11Texture2D* frame_buffer = nullptr;
|
|
swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer);
|
|
if (frame_buffer)
|
|
{
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
frame_buffer->GetDesc(&desc);
|
|
drawable_size = Point(desc.Width, desc.Height);
|
|
|
|
device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view);
|
|
frame_buffer->Release();
|
|
}
|
|
|
|
// TODO:
|
|
// create a depth backbuffer
|
|
|
|
// Store Features
|
|
info.type = RendererType::D3D11;
|
|
info.instancing = true;
|
|
info.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
|
|
info.origin_bottom_left = false;
|
|
|
|
// Print Driver Info
|
|
{
|
|
IDXGIDevice* dxgi_device;
|
|
IDXGIAdapter* dxgi_adapter;
|
|
DXGI_ADAPTER_DESC adapter_desc;
|
|
|
|
hr = device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi_device);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
dxgi_device->GetAdapter(&dxgi_adapter);
|
|
dxgi_adapter->GetDesc(&adapter_desc);
|
|
|
|
Log::info("D3D11 %ls", adapter_desc.Description);
|
|
|
|
dxgi_device->Release();
|
|
dxgi_adapter->Release();
|
|
}
|
|
else
|
|
{
|
|
Log::info("D3D11");
|
|
}
|
|
}
|
|
|
|
// create default sprite batch shader
|
|
default_batcher_shader = Shader::create(d3d11_batch_shader_data);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Renderer_D3D11::shutdown()
|
|
{
|
|
// release cached objects
|
|
for (auto& it : blend_cache)
|
|
it.state->Release();
|
|
for (auto& it : depthstencil_cache)
|
|
it.state->Release();
|
|
for (auto& it : layout_cache)
|
|
it.layout->Release();
|
|
for (auto& it : rasterizer_cache)
|
|
it.state->Release();
|
|
for (auto& it : sampler_cache)
|
|
it.state->Release();
|
|
|
|
// release main devices
|
|
if (backbuffer_view)
|
|
backbuffer_view->Release();
|
|
if (backbuffer_depth_view)
|
|
backbuffer_depth_view->Release();
|
|
swap_chain->Release();
|
|
context->ClearState();
|
|
context->Flush();
|
|
context->Release();
|
|
device->Release();
|
|
}
|
|
|
|
void Renderer_D3D11::update()
|
|
{
|
|
|
|
}
|
|
|
|
void Renderer_D3D11::before_render()
|
|
{
|
|
HRESULT hr;
|
|
|
|
auto next_window_size = App::get_size();
|
|
if (last_window_size != next_window_size)
|
|
{
|
|
last_window_size = next_window_size;
|
|
|
|
// release old buffer
|
|
if (backbuffer_view)
|
|
backbuffer_view->Release();
|
|
|
|
// perform resize
|
|
hr = swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize");
|
|
|
|
// get the new buffer
|
|
ID3D11Texture2D* frame_buffer = nullptr;
|
|
hr = swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer);
|
|
if (SUCCEEDED(hr) && frame_buffer)
|
|
{
|
|
// get backbuffer drawable size
|
|
D3D11_TEXTURE2D_DESC desc;
|
|
frame_buffer->GetDesc(&desc);
|
|
drawable_size = Point(desc.Width, desc.Height);
|
|
|
|
// create view
|
|
hr = device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize");
|
|
frame_buffer->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Renderer_D3D11::get_draw_size(int* w, int* h)
|
|
{
|
|
*w = drawable_size.x;
|
|
*h = drawable_size.y;
|
|
return true;
|
|
}
|
|
|
|
void Renderer_D3D11::after_render()
|
|
{
|
|
auto vsync = App::get_flag(Flags::VSync);
|
|
auto hr = swap_chain->Present(vsync ? 1 : 0, 0);
|
|
BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain");
|
|
}
|
|
|
|
TextureRef Renderer_D3D11::create_texture(int width, int height, TextureFormat format)
|
|
{
|
|
auto result = new D3D11_Texture(width, height, format, false);
|
|
|
|
if (result->texture)
|
|
return TextureRef(result);
|
|
|
|
delete result;
|
|
return TextureRef();
|
|
}
|
|
|
|
TargetRef Renderer_D3D11::create_target(int width, int height, const TextureFormat* attachments, int attachment_count)
|
|
{
|
|
return TargetRef(new D3D11_Target(width, height, attachments, attachment_count));
|
|
}
|
|
|
|
ShaderRef Renderer_D3D11::create_shader(const ShaderData* data)
|
|
{
|
|
auto result = new D3D11_Shader(data);
|
|
if (result->valid)
|
|
return ShaderRef(result);
|
|
|
|
delete result;
|
|
return ShaderRef();
|
|
}
|
|
|
|
MeshRef Renderer_D3D11::create_mesh()
|
|
{
|
|
return MeshRef(new D3D11_Mesh());
|
|
}
|
|
|
|
void Renderer_D3D11::render(const DrawCall& pass)
|
|
{
|
|
auto ctx = 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, &backbuffer_view, backbuffer_depth_view);
|
|
}
|
|
else
|
|
{
|
|
auto target = (D3D11_Target*)(pass.target.get());
|
|
ctx->OMSetRenderTargets(target->color_views.size(), target->color_views.begin(), target->depth_view);
|
|
}
|
|
|
|
// Depth
|
|
{
|
|
auto depthstencil = get_depthstencil(pass);
|
|
if (depthstencil)
|
|
ctx->OMSetDepthStencilState(depthstencil, 0);
|
|
}
|
|
|
|
// Blend Mode
|
|
{
|
|
auto blend = get_blend(pass.blend);
|
|
if (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);
|
|
}
|
|
else
|
|
{
|
|
// if we failed to create a blend mode for some reason
|
|
ctx->OMSetBlendState(nullptr, nullptr, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// IA
|
|
{
|
|
// We draw triangles
|
|
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
|
|
// Assign Layout
|
|
auto layout = 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);
|
|
}
|
|
|
|
// 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->vertex_uniform_buffers.size(), shader->vertex_uniform_buffers.begin());
|
|
}
|
|
|
|
// PS
|
|
{
|
|
apply_uniforms(shader, pass.material, ShaderType::Fragment);
|
|
ctx->PSSetShader(shader->fragment, nullptr, 0);
|
|
ctx->PSSetConstantBuffers(0, shader->fragment_uniform_buffers.size(), shader->fragment_uniform_buffers.begin());
|
|
|
|
// Fragment Shader Textures
|
|
auto& textures = pass.material->textures();
|
|
for (int i = 0; i < textures.size(); i++)
|
|
{
|
|
if (textures[i])
|
|
{
|
|
// Assign the Texture
|
|
auto view = ((D3D11_Texture*)textures[i].get())->view;
|
|
ctx->PSSetShaderResources(i, 1, &view);
|
|
}
|
|
}
|
|
|
|
// Fragment Shader Samplers
|
|
auto& samplers = pass.material->samplers();
|
|
for (int i = 0; i < samplers.size(); i++)
|
|
{
|
|
auto sampler = get_sampler(samplers[i]);
|
|
if (sampler)
|
|
ctx->PSSetSamplers(i, 1, &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 = {
|
|
(int)pass.scissor.x, (int)pass.scissor.y,
|
|
(int)pass.scissor.x + (int)pass.scissor.w, (int)pass.scissor.y + (int)pass.scissor.h };
|
|
|
|
ctx->RSSetScissorRects(1, &scissor);
|
|
}
|
|
else
|
|
{
|
|
ctx->RSSetScissorRects(0, nullptr);
|
|
}
|
|
|
|
// Rasterizer
|
|
{
|
|
auto rasterizer = get_rasterizer(pass);
|
|
if (rasterizer)
|
|
ctx->RSSetState(rasterizer);
|
|
}
|
|
}
|
|
|
|
// Draw
|
|
{
|
|
if (mesh->instance_count() <= 0)
|
|
{
|
|
ctx->DrawIndexed(
|
|
static_cast<UINT>(pass.index_count),
|
|
static_cast<UINT>(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 Renderer_D3D11::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask)
|
|
{
|
|
if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
|
|
{
|
|
float clear[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f };
|
|
context->ClearRenderTargetView(backbuffer_view, clear);
|
|
}
|
|
|
|
if (backbuffer_depth_view)
|
|
{
|
|
UINT flags = 0;
|
|
if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth)
|
|
flags |= D3D11_CLEAR_DEPTH;
|
|
if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil)
|
|
flags |= D3D11_CLEAR_STENCIL;
|
|
|
|
if (flags != 0)
|
|
context->ClearDepthStencilView(backbuffer_depth_view, flags, depth, stencil);
|
|
}
|
|
}
|
|
|
|
// Utility Methods
|
|
|
|
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<UniformInfo>& append_uniforms_to, Vector<ID3D11Buffer*>& append_buffers_to, 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 (UINT 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_uniforms_to.expand();
|
|
uniform->name = desc.Name;
|
|
uniform->shader = shader_type;
|
|
uniform->register_index = desc.BindPoint;
|
|
uniform->buffer_index = 0;
|
|
uniform->array_length = max(1, desc.BindCount);
|
|
uniform->type = UniformType::Texture2D;
|
|
}
|
|
else if (desc.Type == D3D_SIT_SAMPLER)
|
|
{
|
|
auto uniform = append_uniforms_to.expand();
|
|
uniform->name = desc.Name;
|
|
uniform->shader = shader_type;
|
|
uniform->register_index = desc.BindPoint;
|
|
uniform->buffer_index = 0;
|
|
uniform->array_length = max(1, desc.BindCount);
|
|
uniform->type = UniformType::Sampler2D;
|
|
}
|
|
}
|
|
|
|
for (UINT 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;
|
|
renderer->device->CreateBuffer(&buffer_desc, nullptr, &buffer);
|
|
append_buffers_to.push_back(buffer);
|
|
}
|
|
|
|
// get the uniforms
|
|
for (UINT 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_uniforms_to.expand();
|
|
uniform->name = var_desc.Name;
|
|
uniform->shader = shader_type;
|
|
uniform->register_index = 0;
|
|
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;
|
|
}
|
|
|
|
void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type)
|
|
{
|
|
auto& buffers = (type == ShaderType::Vertex ? shader->vertex_uniform_buffers : shader->fragment_uniform_buffers);
|
|
auto& values = (type == ShaderType::Vertex ? shader->vertex_uniform_values : shader->fragment_uniform_values);
|
|
|
|
for (int i = 0; i < buffers.size(); i++)
|
|
{
|
|
// clear previous values
|
|
values[i].clear();
|
|
|
|
// build block
|
|
const float* data = material->data();
|
|
for (auto& it : shader->uniforms())
|
|
{
|
|
if (it.type == UniformType::None ||
|
|
it.type == UniformType::Texture2D ||
|
|
it.type == UniformType::Sampler2D)
|
|
continue;
|
|
|
|
int size = 0;
|
|
switch (it.type)
|
|
{
|
|
case UniformType::None: break;
|
|
case UniformType::Texture2D: break;
|
|
case UniformType::Sampler2D: break;
|
|
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)
|
|
{
|
|
// HLSL uniforms can not pass 16-byte (4-float) boundaries, therefore potentially add padding to the buffer so we align to 16 bytes
|
|
// For example, if we have filled in 6 floats, our remaining space will be 2. If the next value is larger than 2 floats, we will
|
|
// need to move to the next 16-byte (4-float) boundary (in this case 2 floats to begin at 8 floats).
|
|
// see: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
|
|
int remaining = 4 - values[i].size() % 4;
|
|
if (remaining != 4 && remaining + length > 4)
|
|
values[i].expand(remaining);
|
|
|
|
auto start = values[i].expand(length);
|
|
memcpy(start, data, sizeof(float) * length);
|
|
}
|
|
|
|
data += length;
|
|
}
|
|
|
|
// apply block
|
|
if (buffers[i])
|
|
{
|
|
D3D11_MAPPED_SUBRESOURCE map;
|
|
renderer->context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
|
|
memcpy(map.pData, values[i].begin(), values[i].size() * sizeof(float));
|
|
renderer->context->Unmap(buffers[i], 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
ID3D11InputLayout* Renderer_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<D3D11_INPUT_ELEMENT_DESC> 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::None: break;
|
|
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::None: break;
|
|
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* Renderer_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 = 0;
|
|
if (((int)blend.mask & (int)BlendMask::Red) == (int)BlendMask::Red)
|
|
desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_RED;
|
|
if (((int)blend.mask & (int)BlendMask::Green) == (int)BlendMask::Green)
|
|
desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_GREEN;
|
|
if (((int)blend.mask & (int)BlendMask::Blue) == (int)BlendMask::Blue)
|
|
desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_BLUE;
|
|
if (((int)blend.mask & (int)BlendMask::Alpha) == (int)BlendMask::Alpha)
|
|
desc.RenderTarget[0].RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
|
|
|
|
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 = renderer->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;
|
|
}
|
|
|
|
ID3D11SamplerState* Renderer_D3D11::get_sampler(const TextureSampler& sampler)
|
|
{
|
|
for (auto& it : sampler_cache)
|
|
if (it.sampler == sampler)
|
|
return it.state;
|
|
|
|
D3D11_SAMPLER_DESC desc = {};
|
|
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
|
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
|
|
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
|
|
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
|
|
desc.ComparisonFunc = D3D11_COMPARISON_NEVER;
|
|
|
|
switch (sampler.filter)
|
|
{
|
|
case TextureFilter::None: break;
|
|
case TextureFilter::Nearest: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; break;
|
|
case TextureFilter::Linear: desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; break;
|
|
}
|
|
|
|
switch (sampler.wrap_x)
|
|
{
|
|
case TextureWrap::None: break;
|
|
case TextureWrap::Clamp: desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; break;
|
|
case TextureWrap::Repeat: desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; break;
|
|
}
|
|
|
|
switch (sampler.wrap_y)
|
|
{
|
|
case TextureWrap::None: break;
|
|
case TextureWrap::Clamp: desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; break;
|
|
case TextureWrap::Repeat: desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; break;
|
|
}
|
|
|
|
ID3D11SamplerState* result;
|
|
auto hr = renderer->device->CreateSamplerState(&desc, &result);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
auto entry = sampler_cache.expand();
|
|
entry->sampler = sampler;
|
|
entry->state = result;
|
|
return result;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ID3D11RasterizerState* Renderer_D3D11::get_rasterizer(const DrawCall& pass)
|
|
{
|
|
for (auto& it : rasterizer_cache)
|
|
if (it.cull == pass.cull && it.has_scissor == pass.has_scissor)
|
|
return it.state;
|
|
|
|
D3D11_RASTERIZER_DESC desc = {};
|
|
desc.FillMode = D3D11_FILL_SOLID;
|
|
desc.CullMode = D3D11_CULL_NONE;
|
|
|
|
switch (pass.cull)
|
|
{
|
|
case Cull::None: desc.CullMode = D3D11_CULL_NONE; break;
|
|
case Cull::Front: desc.CullMode = D3D11_CULL_FRONT; break;
|
|
case Cull::Back: desc.CullMode = D3D11_CULL_BACK; break;
|
|
}
|
|
|
|
desc.FrontCounterClockwise = true;
|
|
desc.DepthBias = 0;
|
|
desc.DepthBiasClamp = 0;
|
|
desc.SlopeScaledDepthBias = 0;
|
|
desc.DepthClipEnable = false;
|
|
desc.ScissorEnable = pass.has_scissor;
|
|
desc.MultisampleEnable = false;
|
|
desc.AntialiasedLineEnable = false;
|
|
|
|
ID3D11RasterizerState* result;
|
|
auto hr = renderer->device->CreateRasterizerState(&desc, &result);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
auto entry = rasterizer_cache.expand();
|
|
entry->cull = pass.cull;
|
|
entry->has_scissor = pass.has_scissor;
|
|
entry->state = result;
|
|
return result;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ID3D11DepthStencilState* Renderer_D3D11::get_depthstencil(const DrawCall& pass)
|
|
{
|
|
for (auto& it : depthstencil_cache)
|
|
if (it.depth == pass.depth)
|
|
return it.state;
|
|
|
|
D3D11_DEPTH_STENCIL_DESC desc = {};
|
|
desc.DepthEnable = pass.depth != Compare::None;
|
|
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
|
|
desc.DepthFunc = D3D11_COMPARISON_NEVER;
|
|
|
|
switch (pass.depth)
|
|
{
|
|
case Compare::None: desc.DepthFunc = D3D11_COMPARISON_NEVER; break;
|
|
case Compare::Always: desc.DepthFunc = D3D11_COMPARISON_ALWAYS; break;
|
|
case Compare::Never: desc.DepthFunc = D3D11_COMPARISON_NEVER; break;
|
|
case Compare::Less: desc.DepthFunc = D3D11_COMPARISON_LESS; break;
|
|
case Compare::Equal: desc.DepthFunc = D3D11_COMPARISON_EQUAL; break;
|
|
case Compare::LessOrEqual: desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL; break;
|
|
case Compare::Greater: desc.DepthFunc = D3D11_COMPARISON_GREATER; break;
|
|
case Compare::NotEqual: desc.DepthFunc = D3D11_COMPARISON_NOT_EQUAL; break;
|
|
case Compare::GreatorOrEqual: desc.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL; break;
|
|
}
|
|
|
|
ID3D11DepthStencilState* result;
|
|
auto hr = renderer->device->CreateDepthStencilState(&desc, &result);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
auto entry = depthstencil_cache.expand();
|
|
entry->depth = pass.depth;
|
|
entry->state = result;
|
|
return result;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Blah::Renderer* Blah::Renderer::try_make_d3d11()
|
|
{
|
|
return new Blah::Renderer_D3D11();
|
|
}
|
|
|
|
#else // BLAH_RENDERER_D3D11
|
|
|
|
#include "renderer.h"
|
|
Blah::Renderer* Blah::Renderer::try_make_d3d11()
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
#endif
|