Wrapping Class - Pipeline
마지막 Wrapping Class 인 PipeLine이다.
각 부분(IA - VS - RS - PS - OM)을 랜더링 관리를 편하게 하기 위해 Wrapping 한다.
D3D11_PipelineState.h
#pragma once
struct D3D11_PipelineState final
{
D3D11_InputLayout* input_layout = nullptr;
D3D11_PRIMITIVE_TOPOLOGY primitive_topology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
D3D11_Shader* vertex_shader = nullptr;
D3D11_Shader* pixel_shader = nullptr;
D3D11_RasterizerState* rasterizer_state = nullptr;
D3D11_BlendState* blend_state = nullptr;
};
class D3D11_Pipeline final : public Object
{
public:
D3D11_Pipeline(class Graphics* graphics);
~D3D11_Pipeline();
void Begin(const D3D11_PipelineState& pipeline_state);
void End();
void SetVertexBuffer(class D3D11_VertexBuffer* buffer);
void SetIndexBuffer(class D3D11_IndexBuffer* buffer);
void SetConstantBuffer(const uint& slot, const uint& scope, const D3D11_ConstantBuffer* buffer);
void SetShaderResource(const uint& slot, const uint& scope, const D3D11_Texture* resource);
void SetSamplerState(const uint& slot, const uint& scope, const D3D11_SamplerState* state);
void Draw(const uint& vertex_count, const uint& vertex_offset = 0);
void DrawIndexed(const uint& index_count, const uint& index_offset = 0, const uint& vertex_offset = 0);
private:
ID3D11DeviceContext* device_context = nullptr;
uint bind_vertex_buffer_id = 0;
uint bind_index_buffer_id = 0;
uint bind_input_layout_id = 0;
uint bind_primitive_topology_id = 0;
uint bine_rasterizer_state_id = 0;
uint bine_blend_state_id = 0;
};
따로 설정할 필요가 없는 것들을 구조체로 묶어준 뒤,
클래스에서 각 부분들을 받아준다.
마지막에 bind된 변수들은 현재 바인드되어 있는 id값을 저장해준다.
Begin과 End는 뒤에 설명
GUID_Generator.cpp
void D3D11_Pipeline::SetVertexBuffer(D3D11_VertexBuffer* buffer)
{
if (!buffer || !buffer->GetResource())
{
assert(false);
return;
}
if (bind_vertex_buffer_id == buffer->GetID())
{
return;
}
ID3D11Buffer* buffers[] = { buffer->GetResource() };
device_context->IASetVertexBuffers(0, 1, buffers, &buffer->GetStride(), &buffer->GetOffset());
bind_vertex_buffer_id = buffer->GetID();
}
void D3D11_Pipeline::SetIndexBuffer(D3D11_IndexBuffer* buffer)
{
if (!buffer || !buffer->GetResource())
{
assert(false);
return;
}
if (bind_vertex_buffer_id == buffer->GetID())
{
return;
}
device_context->IASetIndexBuffer(buffer->GetResource(), DXGI_FORMAT_R32_UINT, 0);
bind_vertex_buffer_id = buffer->GetID();
}
SetVertexBuffer와 SetIndexBuffer에서는 각 버퍼가 비어있거나 클래스의 리소스가 없으면 종료 시켜주고, 만약 현재 바인드 되어있는 id가 일치하면 세팅을 생략해준다(전에 한걸로 하면 되니까)
그런뒤 현재 바인드 되어있는 id를 갱신시킨다.
void D3D11_Pipeline::SetConstantBuffer(const uint& slot, const uint& scope, const D3D11_ConstantBuffer* buffer)
{
if (!buffer || !buffer->GetResource())
{
assert(false);
return;
}
// Shader를 통해서 constant buffer를 받을 수 있는 슬롯의 갯수
if (slot >= D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT)
{
assert(false);
return;
}
ID3D11Buffer* constant_buffers[] = { buffer->GetResource() };
if (scope & ShaderScope_VS)
{
device_context->VSSetConstantBuffers(slot, 1, constant_buffers);
}
if (scope & ShaderScope_PS)
{
device_context->PSSetConstantBuffers(slot, 1, constant_buffers);
}
}
void D3D11_Pipeline::SetShaderResource(const uint& slot, const uint& scope, const D3D11_Texture* resource)
{
if (!resource || !resource->GetResource())
{
assert(false);
return;
}
// Shader를 통해서 constant buffer를 받을 수 있는 슬롯의 갯수
if (slot >= D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT)
{
assert(false);
return;
}
ID3D11ShaderResourceView* shader_resources[] = { resource->GetResource() };
if (scope & ShaderScope_VS)
{
device_context->VSSetShaderResources(slot, 1, shader_resources);
}
if (scope & ShaderScope_PS)
{
device_context->PSSetShaderResources(slot, 1, shader_resources);
}
}
void D3D11_Pipeline::SetSamplerState(const uint& slot, const uint& scope, const D3D11_SamplerState* state)
{
if (!state || !state->GetResource())
{
assert(false);
return;
}
// Shader를 통해서 constant buffer를 받을 수 있는 슬롯의 갯수
if (slot >= D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT)
{
assert(false);
return;
}
ID3D11SamplerState* sampler_states[] = { state->GetResource() };
if (scope & ShaderScope_VS)
{
device_context->VSSetSamplers(slot, 1, sampler_states);
}
if (scope & ShaderScope_PS)
{
device_context->PSSetSamplers(slot, 1, sampler_states);
}
}
ConstantBuffer, ShaderResource, SamplerState을 세팅하는 부분이다.
D3D11_COMMONSHADER~~ 는 각각 슬롯의 최대 갯수를 리턴해주는 매크로이다.
- D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT : 14
- D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT : 128
- D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT : 16
그리고 매개변수를 자세히 보면 scope를 ShaderScope가 아닌 uint 로 받았다.
왜냐하면 각 부분을 비트마스크로 받아서 한번에 1개 이상 받을수 있도록 하기 위함이다.
ex) scope = ShaderScope_VS | ShaderScope_PS; (물론 l r value 에러가 난다. 예시다 예시) 이렇게 주게 되면 각 부분을 곱해서 있는지 없는지 판단할 수 있도록 해준다.
bool D3D11_Pipeline::Begin(const D3D11_PipelineState& pipeline_state)
{
// Input Layout
{
if (!pipeline_state.input_layout || !pipeline_state.input_layout->GetResource())
{
assert(false);
}
else
{
if (bind_input_layout_id != pipeline_state.input_layout->GetID())
{
device_context->IASetInputLayout(pipeline_state.input_layout->GetResource());
bind_input_layout_id = pipeline_state.input_layout->GetID();
}
}
}
// Primitive Topology
{
if (bind_primitive_topology_id != pipeline_state.primitive_topology)
{
device_context->IASetPrimitiveTopology(pipeline_state.primitive_topology);
bind_primitive_topology_id = static_cast<uint>(pipeline_state.primitive_topology);
}
}
// Vertex Shader
{
if (pipeline_state.vertex_shader)
{
void* new_shader = pipeline_state.vertex_shader->GetResource();
if (new_shader)
{
// 현재 셰이더를 갖고옴
uint instance_count = 256;
ID3D11ClassInstance* instance[256] = { nullptr, };
ID3D11VertexShader* current_shader = nullptr;
device_context->VSGetShader(¤t_shader, instance, &instance_count);
if (current_shader != new_shader)
{
device_context->VSSetShader(static_cast<ID3D11VertexShader*>(new_shader), nullptr, 0);
// buffer와 밀접하기 때문에 초기화
bind_vertex_buffer_id = 0;
bind_index_buffer_id = 0;
}
}
}
}
// Pixel Shader
{
// 현재 셰이더를 갖고옴
uint instance_count = 256;
ID3D11ClassInstance* instance[256] = { nullptr, };
ID3D11PixelShader* current_shader = nullptr;
device_context->PSGetShader(¤t_shader, instance, &instance_count);
void* new_shader = pipeline_state.pixel_shader ? pipeline_state.pixel_shader->GetResource() : nullptr;
if (current_shader != new_shader)
{
device_context->PSSetShader(static_cast<ID3D11PixelShader*>(new_shader), nullptr, 0);
}
}
// Rasterizer State
{
if (pipeline_state.rasterizer_state && pipeline_state.rasterizer_state->GetResource())
{
if (bine_rasterizer_state_id != pipeline_state.rasterizer_state->GetID())
{
device_context->RSSetState(pipeline_state.rasterizer_state->GetResource());
bine_rasterizer_state_id = pipeline_state.rasterizer_state->GetID();
}
}
}
// Blend State
{
if (pipeline_state.blend_state && pipeline_state.blend_state->GetResource())
{
if (bine_blend_state_id != pipeline_state.blend_state->GetID())
{
float factor = pipeline_state.blend_state->GetBlendFactor();
float blend_factor[] = { factor, factor,factor, factor };
uint sample_mask = pipeline_state.blend_state->GetSampleMask();
device_context->OMSetBlendState(pipeline_state.blend_state->GetResource(), blend_factor, sample_mask);
bine_blend_state_id = pipeline_state.blend_state->GetID();
}
}
}
return true;
}
Begin 함수
지금까지 생성한 버퍼와 리소스들을 각 스테이지에 올린다.
Shader부분을 빼면 각자 전에 바인딩한 id를 비교한뒤, 다르면 새로 올린다.
Shader부분은 VertexShader에서 있는지 검사 후 생성,
PixelShader 는 있어도 괜찮고 없어도 괜찮아서 그냥 전에 만든 셰이더랑 같을 경우 생성.