O Diligent Core é uma API gráfica moderna de plataforma cruzada e baixo nível que constitui a base do Diligent Engine. O módulo implementa back-ends de renderização Direct3D11, Direct3D12, OpenGL, OpenGLES e Vulkan (a implementação Metal está disponível para clientes comerciais), bem como utilitários básicos específicos da plataforma. É independente e pode ser construído por conta própria. Consulte o repositório principal para obter informações sobre as plataformas e recursos suportados, instruções de construção, etc.
Plataforma | Status de construção |
---|---|
Win32 | |
Janelas universais | |
Linux | |
Android | |
Mac OS | |
iOS | |
tvOS | |
Escrito |
Clonando o Repositório
Noções básicas de API
Layout de recursos de pipeline
Win32
Plataforma universal do Windows
Linux
Mac OS
Android
iOS
Escrito
Destruindo o motor
Inicializando o mecanismo
Criando Recursos
Criando sombreadores
Inicializando o estado do pipeline
Vinculando recursos de shader
Configurando o estado do pipeline e invocando o comando Draw
Interoperabilidade de API de baixo nível
Instruções de compilação do pacote NuGet
Licença
Contribuindo
Histórico de lançamento
Para obter o repositório e todos os submódulos, use o seguinte comando:
git clone --recursive https://github.com/DiligentGraphics/DiligentCore.git
Para construir o módulo, consulte as instruções de construção no repositório mestre.
Antes de poder usar qualquer funcionalidade fornecida pelo mecanismo, você precisa criar um dispositivo de renderização, um contexto imediato e uma cadeia de troca.
Na plataforma Win32, você pode criar dispositivos OpenGL, Direct3D11, Direct3D12 ou Vulkan conforme mostrado abaixo:
void InitializeDiligentEngine(HWND NativeWindowHandle) { SwapChainDesc SCDesc;// RefCntAutoPtr<IRenderDevice> m_pDevice;// RefCntAutoPtr<IDeviceContext> m_pImmediateContext;// RefCntAutoPtr<ISwapChain> m_pSwapChain;switch (m_DeviceType) {caso RENDER_DEVICE_TYPE_D3D11: { EngineD3D11CreateInfo EngineCI; # if ENGINE_DLL// Carrega a dll e importa GetEngineFactoryD3D11() functionauto* GetEngineFactoryD3D11 = LoadGraphicsEngineD3D11(); # endifauto* pFactoryD3D11 = GetEngineFactoryD3D11(); pFactoryD3D11->CreateDeviceAndContextsD3D11(EngineCI, &m_pDevice, &m_pImmediateContext); Janela Win32NativeWindow{hWnd}; pFactoryD3D11->CreateSwapChainD3D11(m_pDevice, m_pImmediateContext, SCDesc, FullScreenModeDesc{}, Window, &m_pSwapChain); }quebra;caso RENDER_DEVICE_TYPE_D3D12: { # if ENGINE_DLL// Carrega a dll e importa GetEngineFactoryD3D12() functionauto GetEngineFactoryD3D12 = LoadGraphicsEngineD3D12(); # endifEngineD3D12CreateInfo EngineCI;auto* pFactoryD3D12 = GetEngineFactoryD3D12(); pFactoryD3D12->CreateDeviceAndContextsD3D12(EngineCI, &m_pDevice, &m_pImmediateContext); Janela Win32NativeWindow{hWnd}; pFactoryD3D12->CreateSwapChainD3D12(m_pDevice, m_pImmediateContext, SCDesc, FullScreenModeDesc{}, Window, &m_pSwapChain); }quebra;caso RENDER_DEVICE_TYPE_GL: { # if EXPLICITLY_LOAD_ENGINE_GL_DLL// Carrega a dll e importa GetEngineFactoryOpenGL() functionauto GetEngineFactoryOpenGL = LoadGraphicsEngineOpenGL(); # endifauto* pFactoryOpenGL = GetEngineFactoryOpenGL(); EngineGLCreateInfo EngineCI; MotorCI.Window.hWnd = hWnd; pFactoryOpenGL->CreateDeviceAndSwapChainGL(EngineCI, &m_pDevice, &m_pImmediateContext, SCDesc, &m_pSwapChain); }quebra;caso RENDER_DEVICE_TYPE_VULKAN: { # if EXPLICITLY_LOAD_ENGINE_VK_DLL// Carrega a dll e importa GetEngineFactoryVk() functionauto GetEngineFactoryVk = LoadGraphicsEngineVk(); # endifEngineVkCreateInfo EngineCI;auto* pFactoryVk = GetEngineFactoryVk(); pFactoryVk->CreateDeviceAndContextsVk(EngineCI, &m_pDevice, &m_pImmediateContext); Janela Win32NativeWindow{hWnd}; pFactoryVk->CreateSwapChainVk(m_pDevice, m_pImmediateContext, SCDesc, Window, &m_pSwapChain); }quebrar;padrão: std::cerr << "Tipo de dispositivo desconhecido"; } }
No Windows, o mecanismo pode ser vinculado estaticamente ao aplicativo ou criado como uma DLL separada. No primeiro caso, as funções de fábrica GetEngineFactoryOpenGL()
, GetEngineFactoryD3D11()
, GetEngineFactoryD3D12()
e GetEngineFactoryVk()
podem ser chamadas diretamente. No segundo caso, você precisa carregar a DLL no espaço de endereço do processo usando a função LoadGraphicsEngineOpenGL()
, LoadGraphicsEngineD3D11()
, LoadGraphicsEngineD3D12()
ou LoadGraphicsEngineVk()
. Cada função carrega a biblioteca dinâmica apropriada e importa as funções necessárias para inicializar o mecanismo. Você precisa incluir os seguintes cabeçalhos:
#include "EngineFactoryD3D11.h"#include "EngineFactoryD3D12.h"#include "EngineFactoryOpenGL.h"#include "EngineFactoryVk.h"
Você também precisa adicionar os seguintes diretórios aos caminhos de pesquisa incluídos:
DiligentCore/Graphics/GraphicsEngineD3D11/interface
DiligentCore/Graphics/GraphicsEngineD3D12/interface
DiligentCore/Graphics/GraphicsEngineOpenGL/interface
DiligentCore/Graphics/GraphicsEngineVulkan/interface
Como alternativa, você só pode adicionar o caminho à pasta raiz e depois usar caminhos de inclusão relativos a ela.
Ative o namespace Diligent
:
usando o namespace Diligent;
As funções IEngineFactoryD3D11::CreateDeviceAndContextsD3D11()
, IEngineFactoryD3D12::CreateDeviceAndContextsD3D12()
e IEngineFactoryVk::CreateDeviceAndContextsVk()
também podem criar um número especificado de contextos imediatos e adiados, que podem ser usados para renderização assíncrona e comando multithread. gravação. Os contextos só podem ser criados durante a inicialização do motor. A função preenche uma matriz de ponteiros para os contextos, onde os contextos imediatos vão primeiro, seguidos por todos os contextos adiados.
Para mais detalhes, dê uma olhada no arquivo Tutorial00_HelloWin32.cpp.
Na Plataforma Universal do Windows, você pode criar dispositivos Direct3D11 ou Direct3D12. A inicialização é realizada da mesma forma que na plataforma Win32. A diferença é que você primeiro cria o dispositivo de renderização e os contextos do dispositivo chamando IEngineFactoryD3D11::CreateDeviceAndContextsD3D11()
ou IEngineFactoryD3D12::CreateDeviceAndContextsD3D12()
. A cadeia de troca é criada posteriormente por uma chamada para IEngineFactoryD3D11::CreateSwapChainD3D11()
ou IEngineFactoryD3D12::CreateSwapChainD3D12()
. Consulte o arquivo SampleAppUWP.cpp para obter mais detalhes.
Na plataforma Linux, o mecanismo oferece suporte a back-ends OpenGL e Vulkan. A inicialização do contexto GL no Linux está fortemente associada à criação de janelas. Como resultado, o Diligent Engine não inicializa o contexto, mas anexa-se àquele inicializado pelo aplicativo. Um exemplo de inicialização do motor no Linux pode ser encontrado em Tutorial00_HelloLinux.cpp.
No MacOS, o Diligent Engine oferece suporte a back-ends OpenGL, Vulkan e Metal. A inicialização do contexto GL no MacOS é realizada pelo aplicativo e o mecanismo se conecta ao contexto criado pelo aplicativo; consulte GLView.mm para obter detalhes. O back-end do Vulkan é inicializado de forma semelhante a outras plataformas. Consulte MetalView.mm.
No Android, você pode criar dispositivos OpenGLES ou Vulkan. O trecho de código a seguir mostra um exemplo:
automático* pFactoryOpenGL = GetEngineFactoryOpenGL(); EngineGLCreateInfo EngineCI; EngineCI.Window.pAWindow = NativeWindowHandle; pFactoryOpenGL->CreateDeviceAndSwapChainGL( EngineCI, &m_pDevice, &m_pContext, SCDesc, &m_pSwapChain);
Se o mecanismo for construído como uma biblioteca dinâmica, a biblioteca precisará ser carregada pela atividade nativa. O código a seguir mostra uma maneira possível:
static{tente{System.loadLibrary("GraphicsEngineOpenGL"); } catch (UnsatisfiedLinkError e) {Log.e("atividade nativa", "Falha ao carregar biblioteca GraphicsEngineOpenGL.n" + e); } }
A implementação do iOS suporta back-end OpenGLES, Vulkan e Metal. A inicialização do contexto GL no iOS é realizada pelo aplicativo e o mecanismo se conecta ao contexto inicializado pelo aplicativo; consulte EAGLView.mm para obter detalhes.
No Emscripten, você pode criar um dispositivo OpenGLES. O trecho de código a seguir mostra um exemplo:
//Você precisa passar o id do canvas para NativeWindowauto* pFactoryOpenGL = GetEngineFactoryOpenGL(); EngineGLCreateInfo EngineCI = {}; EngineCI.Window = NativeWindow{"#canvas"}; pFactoryOpenGL->CreateDeviceAndSwapChainGL(EngineCI, &m_pDevice, &m_pContext, SCDesc, &m_pSwapChain);
Se você estiver usando SDL ou GLFW com contexto existente, poderá fornecer null como o identificador de janela nativo: EngineCI.Window = NativeWindow{nullptr}
O mecanismo executa a contagem automática de referências e desliga quando a última referência a um objeto do mecanismo é liberada.
Os recursos do dispositivo são criados pelo dispositivo de renderização. Os dois principais tipos de recursos são buffers, que representam memória linear, e texturas, que usam layouts de memória otimizados para filtragem rápida. Para criar um buffer, você precisa preencher a estrutura BufferDesc
e chamar IRenderDevice::CreateBuffer()
. O código a seguir cria um buffer uniforme (constante):
BufferDesc BuffDesc; BuffDesc.Name = "Buffer uniforme"; BuffDesc.BindFlags = BIND_UNIFORM_BUFFER; BuffDesc.Usage = USAGE_DYNAMIC; BuffDesc.uiSizeInBytes = sizeof(ShaderConstants); BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; m_pDevice->CreateBuffer(BuffDesc, nullptr, &m_pConstantBuffer);
Da mesma forma, para criar uma textura, preencha a estrutura TextureDesc
e chame IRenderDevice::CreateTexture()
como no exemplo a seguir:
TexturaDesc TexDesc; TexDesc.Name = "Minha textura 2D"; TexDesc.Type = TEXTURE_TYPE_2D; TexDesc.Largura = 1024; TexDesc.Altura = 1024; TexDesc.Format = TEX_FORMAT_RGBA8_UNORM; TexDesc.Usage = USAGE_DEFAULT; TexDesc.BindFlags=BIND_SHADER_RESOURCE | BIND_RENDER_TARGET | BIND_UNORDERED_ACCESS; TexDesc.Name = "Exemplo de Textura 2D"; m_pRenderDevice->CreateTexture(TexDesc, nullptr, &m_pTestTex);
Existe apenas uma função CreateTexture()
que é capaz de criar todos os tipos de texturas. Tipo, formato, tamanho do array e todos os outros parâmetros são especificados pelos membros da estrutura TextureDesc
.
Para cada sinalizador de ligação especificado durante o tempo de criação da textura, o objeto de textura cria uma visualização padrão. A visualização de recurso de sombreador padrão aborda toda a textura, as visualizações de destino de renderização padrão e de estêncil de profundidade fazem referência a todas as fatias da matriz no nível mip mais detalhado e a visualização de acesso não ordenado faz referência a toda a textura. Para obter uma visualização padrão da textura, use a função ITexture::GetDefaultView()
. Observe que esta função não incrementa o contador de referência da interface retornada. Você pode criar visualizações de textura adicionais usando ITexture::CreateView()
. Use IBuffer::CreateView()
para criar visualizações adicionais de um buffer.
Para criar um shader, preencha a estrutura ShaderCreateInfo
:
ShaderCreateInfo ShaderCI;
Existem três maneiras de criar um shader. A primeira maneira é fornecer um ponteiro para o código-fonte do shader por meio do membro ShaderCreateInfo::Source
. A segunda maneira é fornecer um nome de arquivo. A terceira maneira é fornecer um ponteiro para o código de bytes compilado por meio do membro ShaderCreateInfo::ByteCode
. O Graphics Engine é totalmente dissociado da plataforma. Como o sistema de arquivos host depende da plataforma, a estrutura expõe o membro ShaderCreateInfo::pShaderSourceStreamFactory
que se destina a fornecer ao mecanismo acesso ao sistema de arquivos. Se você forneceu o nome do arquivo de origem, também deverá fornecer um ponteiro não nulo para a fábrica de fluxo de origem do sombreador. Se a fonte do shader contiver alguma diretiva #include
, a fábrica do fluxo de origem também será usada para carregar esses arquivos. O mecanismo fornece implementação padrão para cada plataforma suportada, o que deve ser suficiente na maioria dos casos. No entanto, você pode definir sua própria implementação.
Um membro importante é ShaderCreateInfo::SourceLanguage
. A seguir estão os valores válidos para este membro:
SHADER_SOURCE_LANGUAGE_DEFAULT
- O formato de origem do shader corresponde à API gráfica subjacente: HLSL para modo D3D11 ou D3D12 e GLSL para modos OpenGL, OpenGLES e Vulkan.
SHADER_SOURCE_LANGUAGE_HLSL
– A origem do shader está em HLSL. Para os modos OpenGL e OpenGLES, o código fonte será convertido para GLSL. No back-end do Vulkan, o código será compilado diretamente no SPIRV.
SHADER_SOURCE_LANGUAGE_GLSL
- A fonte do shader está em GLSL.
SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM
- A linguagem fonte do shader é GLSL e deve ser compilada literalmente.
SHADER_SOURCE_LANGUAGE_MSL
- O idioma de origem é Metal Shading Language.
Outros membros da estrutura ShaderCreateInfo
definem o sombreador, incluindo diretórios de pesquisa, definições de macro de sombreador, ponto de entrada do sombreador e outros parâmetros.
Macros ShaderMacroHelper; Macros.AddShaderMacro("USE_SHADOWS", 1); Macros.AddShaderMacro("NUM_SHADOW_SAMPLES", 4); Macros.Finalize(); ShaderCI.Macros = Macros;
Quando tudo estiver pronto, chame IRenderDevice::CreateShader()
para criar o objeto shader:
ShaderCreateInfo ShaderCI; ShaderCI.Desc.Name = "MeuPixelShader"; ShaderCI.FilePath = "MeuShaderFile.fx"; ShaderCI.EntryPoint = "MeuPixelShader"; ShaderCI.Desc.ShaderType = SHADER_TYPE_PIXEL; ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;const auto* SearchDirectories = "shaders;shadersinc;"; RefCntAutoPtr<IShaderSourceInputStreamFactory> pShaderSourceFactory; m_pEngineFactory->CreateDefaultShaderSourceStreamFactory(SearchDirectories, &pShaderSourceFactory); ShaderCI.pShaderSourceStreamFactory = pShaderSourceFactory; RefCntAutoPtr<IShader> pShader; m_pDevice->CreateShader(ShaderCI, &pShader);
O Diligent Engine segue o estilo Direct3D12/Vulkan para configurar o pipeline gráfico/computação. Um Pipelines State Object (PSO) monolítico abrange todos os estados necessários (todos os estágios de shader, descrição do layout de entrada, estêncil de profundidade, rasterizador e descrições de estado de mistura, etc.). Para criar um objeto de estado de pipeline gráfico, defina uma instância da estrutura GraphicsPipelineStateCreateInfo
:
GraphicsPipelineStateCreateInfo PSOCreateInfo; PipelineStateDesc& PSODesc = PSOCreateInfo.PSODesc; PSODesc.Name = "Meu estado de pipeline";
Descreva as especificações do pipeline, como o número e o formato dos alvos de renderização, bem como o formato do estêncil de profundidade:
// Este é um pipeline gráficoPSODesc.PipelineType = PIPELINE_TYPE_GRAPHICS; PSOCreateInfo.GraphicsPipeline.NumRenderTargets = 1; PSOCreateInfo.GraphicsPipeline.RTVFormats[0] = TEX_FORMAT_RGBA8_UNORM_SRGB; PSOCreateInfo.GraphicsPipeline.DSVFormat = TEX_FORMAT_D32_FLOAT;
Inicialize a descrição do estado do estêncil de profundidade DepthStencilStateDesc
. Observe que o construtor inicializa os membros com valores padrão e você só pode definir aqueles que são diferentes do padrão.
// Inicialização do estêncil de profundidade stateDepthStencilStateDesc& DepthStencilDesc = PSOCreateInfo.GraphicsPipeline.DepthStencilDesc; DepthStencilDesc.DepthEnable = verdadeiro; DepthStencilDesc.DepthWriteEnable = verdadeiro;
Inicializar descrição do estado de mistura BlendStateDesc
:
// Inicia a mistura stateBlendStateDesc& BSDesc = PSOCreateInfo.GraphicsPipeline.BlendDesc; BSDesc.IndependentBlendEnable = False;auto &RT0 = BSDesc.RenderTargets[0]; RT0.BlendEnable = Verdadeiro; RT0.RenderTargetWriteMask=COLOR_MASK_ALL; RT0.SrcBlend = BLEND_FACTOR_SRC_ALPHA; RT0.DestBlend = BLEND_FACTOR_INV_SRC_ALPHA; RT0.BlendOp = BLEND_OPERATION_ADD; RT0.SrcBlendAlpha = BLEND_FACTOR_SRC_ALPHA; RT0.DestBlendAlpha = BLEND_FACTOR_INV_SRC_ALPHA; RT0.BlendOpAlpha = BLEND_OPERATION_ADD;
Inicializar descrição do estado do rasterizador RasterizerStateDesc
:
// Inicialização do rasterizador stateRasterizerStateDesc& RasterizerDesc = PSOCreateInfo.GraphicsPipeline.RasterizerDesc; RasterizerDesc.FillMode = FILL_MODE_SOLID; RasterizerDesc.CullMode = CULL_MODE_NONE; RasterizerDesc.FrontCounterClockwise = Verdadeiro; RasterizerDesc.ScissorEnable = Verdadeiro; RasterizerDesc.AntialiasedLineEnable = Falso;
Inicialize a descrição do layout de entrada InputLayoutDesc
:
// Define a entrada layoutInputLayoutDesc& Layout = PSOCreateInfo.GraphicsPipeline.InputLayout; LayoutElement LayoutElems[] = {LayoutElement( 0, 0, 3, VT_FLOAT32, False ),LayoutElement( 1, 0, 4, VT_UINT8, True ),LayoutElement( 2, 0, 2, VT_FLOAT32, False ), }; Layout.LayoutElements = LayoutElems; Layout.NumElements = _countof(LayoutElems);
Defina a topologia primitiva e defina ponteiros de shader:
// Definir shader e topologia primitivaPSOCreateInfo.GraphicsPipeline.PrimitiveTopology = PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; PSOCreateInfo.pVS = m_pVS; PSOCreateInfo.pPS = m_pPS;
O layout de recursos do pipeline informa ao mecanismo como o aplicativo usará diferentes variáveis de recursos de shader. Para permitir o agrupamento de recursos com base na frequência esperada de alterações nas vinculações de recursos, o Diligent Engine introduz a classificação de variáveis de sombreador:
Variáveis estáticas ( SHADER_RESOURCE_VARIABLE_TYPE_STATIC
) são variáveis que devem ser definidas apenas uma vez. Eles não podem ser alterados quando um recurso estiver vinculado à variável. Tais variáveis destinam-se a manter constantes globais, como atributos de câmera ou buffers constantes de atributos de luz global. Observe que é a ligação do recurso que não pode mudar, enquanto o conteúdo do recurso pode mudar de acordo com seu uso.
Variáveis mutáveis ( SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE
) definem recursos que devem mudar em uma frequência por material. Os exemplos podem incluir texturas difusas, mapas normais, etc. Novamente, as atualizações no conteúdo do recurso são ortogobais às alterações de ligação.
Espera-se que variáveis dinâmicas ( SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC
) mudem com frequência e aleatoriamente.
Para definir tipos de variáveis, prepare uma matriz de estruturas ShaderResourceVariableDesc
e inicialize os membros PSODesc.ResourceLayout.Variables
e PSODesc.ResourceLayout.NumVariables
. Além disso, PSODesc.ResourceLayout.DefaultVariableType
pode ser usado para definir o tipo que será usado se um nome de variável não for fornecido.
ShaderResourceVariableDesc ShaderVars[] = { {SHADER_TYPE_PIXEL, "g_StaticTexture", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}, {SHADER_TYPE_PIXEL, "g_MutableTexture", SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE}, {SHADER_TYPE_PIXEL, "g_DynamicTexture", SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC} }; PSODesc.ResourceLayout.Variables = ShaderVars; PSODesc.ResourceLayout.NumVariables = _countof(ShaderVars); PSODesc.ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC;
Ao criar um estado de pipeline, as texturas podem receber amostradores imutáveis permanentemente atribuídos. Se um amostrador imutável for atribuído a uma textura, ele sempre será usado em vez daquele inicializado na visualização de recursos do sombreador de textura. Para definir amostradores imutáveis, prepare uma matriz de estruturas ImmutableSamplerDesc
e inicialize os membros PSODesc.ResourceLayout.ImmutableSamplers
e PSODesc.ResourceLayout.NumImmutableSamplers
. Observe que os amostradores imutáveis podem ser atribuídos a uma variável de textura de qualquer tipo, não necessariamente estática, de modo que a ligação da textura possa ser alterada em tempo de execução, enquanto o amostrador permanecerá imutável. É altamente recomendável usar amostradores imutáveis sempre que possível.
ImmutableSamplerDesc ImtblSampler; ImtblSampler.ShaderStages = SHADER_TYPE_PIXEL; ImtblSampler.Desc.MinFilter = FILTER_TYPE_LINEAR; ImtblSampler.Desc.MagFilter = FILTER_TYPE_LINEAR; ImtblSampler.Desc.MipFilter = FILTER_TYPE_LINEAR; ImtblSampler.TextureName = "g_MutableTexture"; PSODesc.ResourceLayout.NumImmutableSamplers = 1; PSODesc.ResourceLayout.ImmutableSamplers = &ImtblSampler;
Este documento fornece informações detalhadas sobre como trabalhar com amostradores de textura.
Quando todos os campos obrigatórios da estrutura de descrição do PSO estiverem definidos, chame IRenderDevice::CreateGraphicsPipelineState()
para criar o objeto PSO:
m_pDevice->CreateGraphicsPipelineState(PSOCreateInfo, &m_pPSO);
Conforme mencionado acima, a vinculação de recursos de sombreador no Diligent Engine é baseada no agrupamento de variáveis em três grupos diferentes (estático, mutável e dinâmico). Variáveis estáticas são variáveis que devem ser definidas apenas uma vez. Eles não podem ser alterados quando um recurso estiver vinculado à variável. Tais variáveis destinam-se a manter constantes globais, como atributos de câmera ou buffers constantes de atributos de luz global. Eles estão vinculados diretamente ao objeto de estado do pipeline:
m_pPSO->GetStaticShaderVariable(SHADER_TYPE_PIXEL, "g_tex2DShadowMap")->Set(pShadowMapSRV);
Variáveis mutáveis e dinâmicas são vinculadas por meio de um novo objeto chamado Shader Resource Binding (SRB), que é criado pelo estado do pipeline ( IPipelineState::CreateShaderResourceBinding()
) ou assinatura de recurso do pipeline em casos de uso avançados:
m_pPSO->CreateShaderResourceBinding(&m_pSRB, verdadeiro);
O segundo parâmetro informa ao sistema para inicializar estruturas internas no objeto SRB que fazem referência a variáveis estáticas no PSO.
Recursos dinâmicos e mutáveis são então vinculados por meio do objeto SRB:
m_pSRB->GetVariable(SHADER_TYPE_PIXEL, "tex2DDiffuse")->Set(pDiffuseTexSRV); m_pSRB->GetVariable(SHADER_TYPE_VERTEX, "cbRandomAttribs")->Set(pRandomAttrsCB);
A diferença entre recursos mutáveis e dinâmicos é que os recursos mutáveis só podem ser definidos uma vez por instância de uma ligação de recurso de sombreador. Os recursos dinâmicos podem ser definidos diversas vezes. É importante definir corretamente o tipo de variável, pois isso afeta o desempenho. Variáveis estáticas e mutáveis são mais eficientes. Variáveis dinâmicas são mais caras e introduzem alguma sobrecarga no tempo de execução.
Uma maneira alternativa de vincular recursos de sombreador é criar uma interface IResourceMapping
que mapeie nomes literais de recursos para os recursos reais:
Entradas ResourceMappingEntry[] = { {"g_Texture", pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)} }; ResourceMappingCreateInfo ResMappingCI; ResMappingCI.pEntries = Entradas; ResMappingCI.NumEntries = _countof(Entradas); RefCntAutoPtr<IResourceMapping> pResMapping; pRenderDevice->CreateResourceMapping(ResMappingCI, &pResMapping);
O mapeamento de recursos pode então ser usado para vincular todos os recursos estáticos em um estado de pipeline ( IPipelineState::BindStaticResources()
):
m_pPSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
ou todos os recursos mutáveis e dinâmicos em uma ligação de recursos de shader ( IShaderResourceBinding::BindResources()
):
m_pSRB->BindResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
O último parâmetro de todas as funções BindResources()
define como os recursos devem ser resolvidos:
BIND_SHADER_RESOURCES_UPDATE_STATIC
– Indica que as ligações de variáveis estáticas devem ser atualizadas.
BIND_SHADER_RESOURCES_UPDATE_MUTABLE
- Indica que as ligações de variáveis mutáveis devem ser atualizadas.
BIND_SHADER_RESOURCES_UPDATE_DYNAMIC
-Indica que as ligações de variáveis dinâmicas devem ser atualizadas.
BIND_SHADER_RESOURCES_UPDATE_ALL
- Indica que todos os tipos de variáveis (estáticas, mutáveis e dinâmicas) devem ser atualizadas. Observe que se nenhum dos sinalizadores BIND_SHADER_RESOURCES_UPDATE_STATIC
, BIND_SHADER_RESOURCES_UPDATE_MUTABLE
e BIND_SHADER_RESOURCES_UPDATE_DYNAMIC
estiver definido, todos os tipos de variáveis serão atualizados como se BIND_SHADER_RESOURCES_UPDATE_ALL
tivesse sido especificado.
BIND_SHADER_RESOURCES_KEEP_EXISTING
– Se este sinalizador for especificado, apenas as ligações não resolvidas serão atualizadas. Todas as ligações existentes manterão seus valores originais. Se este sinalizador não for especificado, cada variável de shader será atualizada se o mapeamento contiver o recurso correspondente.
BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED
- Se esse sinalizador for especificado, espera-se que todas as ligações de shader sejam resolvidas após a chamada. Se este não for o caso, um erro será relatado.
BindResources()
pode ser chamado várias vezes com diferentes mapeamentos de recursos para vincular recursos. No entanto, é recomendado usar um mapeamento de recursos grande, pois o tamanho do mapeamento não afeta o tempo de pesquisa do elemento.
O mecanismo executa verificações em tempo de execução para verificar se os recursos corretos estão sendo vinculados. Por exemplo, se você tentar vincular um buffer constante a uma variável de visualização de recursos do sombreador, um erro será gerado no console de depuração.
Antes que qualquer comando de desenho possa ser invocado, todos os vértices e buffers de índice necessários, bem como o estado do pipeline, devem ser vinculados ao contexto do dispositivo:
// Defina alvos de renderização antes de emitir qualquer comando de desenho.auto* pRTV = m_pSwapChain->GetCurrentBackBufferRTV();auto* pDSV = m_pSwapChain->GetDepthBufferDSV(); m_pContext->SetRenderTargets(1, &pRTV, pDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);// Limpar destino de renderização e profundidade-stencilconst float zero[4] = {0, 0, 0, 0}; m_pContext->ClearRenderTarget(pRTV, ClearColor, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); m_pContext->ClearDepthStencil(pDSV, CLEAR_DEPTH_FLAG, 1.f, 0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);// Definir buffers de vértice e índiceIBuffer* buffer[] = {m_pVertexBuffer}; Deslocamentos Uint32[] = {0}; m_pContext->SetVertexBuffers(0, 1, buffer, deslocamentos, SET_VERTEX_BUFFERS_FLAG_RESET, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); m_pContext->SetIndexBuffer(m_pIndexBuffer, 0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); m_pContext->SetPipelineState(m_pPSO);
Todos os métodos que podem precisar realizar transições de estado de recurso usam enum RESOURCE_STATE_TRANSITION_MODE
como parâmetro. O enum define os seguintes modos:
RESOURCE_STATE_TRANSITION_MODE_NONE
– Não executa transições de estado de recurso.
RESOURCE_STATE_TRANSITION_MODE_TRANSITION
- Transição de recursos para os estados exigidos pelo comando.
RESOURCE_STATE_TRANSITION_MODE_VERIFY
– Não faça a transição, mas verifique se os estados estão corretos.
A etapa final é comprometer os recursos do shader no contexto do dispositivo. Isso é feito pelo método IDeviceContext::CommitShaderResources()
:
m_pContext->CommitShaderResources(m_pSRB, COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES);
Se o método não for chamado, o mecanismo detectará que os recursos não foram confirmados e gerará uma mensagem de depuração. Observe que o último parâmetro informa ao sistema para fazer a transição dos recursos para estados corretos. Se esse sinalizador não for especificado, os recursos deverão passar explicitamente pela transição para os estados necessários por meio de uma chamada para IDeviceContext::TransitionShaderResources()
:
m_pContext->TransitionShaderResources(m_pPSO, m_pSRB);
Observe que o método requer um ponteiro para o estado do pipeline que criou a ligação do recurso do sombreador.
Quando todos os estados e recursos necessários estão vinculados, IDeviceContext::DrawIndexed()
pode ser usado para executar um comando de desenho ou IDeviceContext::DispatchCompute()
pode ser usado para executar um comando de computação. Observe que para um comando de desenho, um pipeline gráfico deve ser vinculado e, para um comando de expedição, um pipeline de computação deve ser vinculado. DrawIndexed()
usa a estrutura DrawIndexedAttribs
como argumento, por exemplo:
Atributos DrawIndexedAttribs; attrs.IndexType = VT_UINT16; attrs.NumIndices = 36; attrs.Flags = DRAW_FLAG_VERIFY_STATES; pContext->DrawIndexed(attrs);
O sinalizador DRAW_FLAG_VERIFY_STATES
instrui o mecanismo a verificar se os buffers de vértice e índice usados pelo comando draw foram transferidos para os estados adequados.
DispatchCompute()
usa a estrutura DispatchComputeAttribs
que define as dimensões da grade de computação:
m_pContext->SetPipelineState(m_pComputePSO); m_pContext->CommitShaderResources(m_pComputeSRB, COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES); DispatchComputeAttribs DispatchAttrs{64, 64, 8}; m_pContext->DispatchCompute(DispatchAttrs);
Você pode aprender mais sobre a API do mecanismo estudando exemplos e tutoriais.
O Diligent Engine oferece amplo suporte à interoperabilidade com APIs subjacentes de baixo nível. O mecanismo pode ser inicializado anexando-se ao dispositivo D3D11/D3D12 existente ou ao contexto OpenGL/GLES e fornece acesso aos objetos API nativos subjacentes. Consulte as páginas a seguir para obter mais informações:
Interoperabilidade Direct3D11
Interoperabilidade Direct3D12
Interoperabilidade OpenGL/GLES
Interoperabilidade Vulkan
Siga as etapas a seguir para criar o pacote NuGet:
Instale os pacotes Python necessários
python -m pip install -r ./BuildTools/.NET/requirements.txt
Execute o script de criação do pacote NuGet, por exemplo:
python ./BuildTools/.NET/dotnet-build-package.py -c Debug -d ./
Argumento | Descrição | Obrigatório |
---|---|---|
-c ( configuration ) | Configuração de construção de bibliotecas dinâmicas nativas (por exemplo, Debug, Release, etc.) | Sim |
-d ( root-dir ) | O caminho para o diretório raiz do DiligentCore | Sim |
-s ( settings ) | O caminho para o arquivo de configurações | Não |
dotnet-tests | Sinalizador indicando se deve executar testes .NET | Não |
dotnet-publish | Sinalizador que indica se o pacote deve ser publicado na Galeria NuGet | Não |
free-memory | Use este argumento se você encontrar memória insuficiente durante o processo de construção | Não |
Você pode substituir as configurações padrão usando um arquivo de configurações (verifique o dicionário default_settings
em dotnet-build-package.py
)
Consulte licença Apache 2.0.
Este projeto possui algumas dependências de terceiros, cada uma das quais pode ter licenciamento independente:
Vulkan-Headers: Arquivos de cabeçalho Vulkan e registro de API (Licença Apache 2.0).
SPIRV-Cross: Ferramentas de análise e compilação cruzada SPIRV (Licença Apache 2.0).
Cabeçalhos SPIRV: arquivos de cabeçalho SPIRV (licença semelhante ao Khronos MIT).
Ferramentas SPIRV: Ferramentas de otimização e validação SPIRV (Licença Apache 2.0).
glslang: Compilador de referência Khronos e validador para GLSL, ESSL e HLSL (Licença BSD de 3 cláusulas, Licença BSD de 2 cláusulas, MIT, Licença Apache 2.0).
glew: OpenGL Extension Wrangler Library (biblioteca gráfica Mesa 3-D, licença semelhante ao Khronos MIT).
volk: Metaloader para API Vulkan (licença semelhante a Arseny Kapoulkine MIT).
stb: bibliotecas stb de domínio público de arquivo único para C/C++ (licença MIT ou domínio público).
googletest: Google Testing and Mocking Framework (licença BSD de 3 cláusulas "Nova" ou "Revisada").
DirectXShaderCompiler: Compilador DirectX Shader baseado em LLVM/Clang (licença de lançamento LLVM).
DXBCChecksum: Algoritmo de cálculo DXBC Checksum da AMD Developer Tools Team (MIT lincesne).
xxHash: Algoritmo hash não criptográfico extremamente rápido (licença BSD de 2 cláusulas).
Para contribuir com seu código, envie uma solicitação pull para este repositório. O Diligent Engine é distribuído sob a licença Apache 2.0, que garante que o conteúdo do repositório DiligentCore esteja livre de gravames de propriedade intelectual. Ao enviar qualquer conteúdo para este repositório, você licencia esse conteúdo sob os mesmos termos e concorda que o conteúdo está livre de quaisquer reivindicações de propriedade intelectual e tem o direito de licenciá-lo sob esses termos.
O Diligent Engine usa o formato clang para garantir um estilo de código-fonte consistente em toda a base de código. O formato é validado pelo CI para cada solicitação de confirmação e pull, e a construção falhará se algum problema de formatação de código for encontrado. Consulte esta página para obter instruções sobre como configurar o formato clang e a formatação automática de código.
Veja o histórico de lançamentos
diligentgraphics.com