Diligent Core est une API graphique multiplateforme moderne de bas niveau qui constitue la base de Diligent Engine. Le module implémente les backends de rendu Direct3D11, Direct3D12, OpenGL, OpenGLES et Vulkan (l'implémentation Metal est disponible pour les clients commerciaux), ainsi que des utilitaires de base spécifiques à la plate-forme. Il est autonome et peut être construit seul. Veuillez vous référer au référentiel principal pour plus d'informations sur les plates-formes et fonctionnalités prises en charge, les instructions de construction, etc.
Plate-forme | Statut de construction |
---|---|
Win32 | |
Fenêtres universelles | |
Linux | |
Androïde | |
Mac OS | |
IOS | |
tvOS | |
Emscripten |
Clonage du référentiel
Bases de l'API
Disposition des ressources du pipeline
Win32
Plateforme Windows universelle
Linux
Mac OS
Androïde
IOS
Emscripten
Détruire le moteur
Initialisation du moteur
Création de ressources
Création de shaders
Initialisation de l'état du pipeline
Ressources de shader de liaison
Définition de l'état du pipeline et appel de la commande Draw
Interopérabilité des API de bas niveau
Instructions de création du package NuGet
Licence
Contribuer
Historique des versions
Pour obtenir le référentiel et tous les sous-modules, utilisez la commande suivante :
git clone --recursive https://github.com/DiligentGraphics/DiligentCore.git
Pour construire le module, consultez les instructions de construction dans le référentiel maître.
Avant de pouvoir utiliser une fonctionnalité fournie par le moteur, vous devez créer un périphérique de rendu, un contexte immédiat et une chaîne d'échange.
Sur la plateforme Win32, vous pouvez créer un périphérique OpenGL, Direct3D11, Direct3D12 ou Vulkan comme indiqué ci-dessous :
void InitializeDiligentEngine (HWND NativeWindowHandle) { SwapChainDesc SCDesc;// RefCntAutoPtr<IRenderDevice> m_pDevice;// RefCntAutoPtr<IDeviceContext> m_pImmediateContext;// RefCntAutoPtr<ISwapChain> m_pSwapChain;switch (m_DeviceType) {cas RENDER_DEVICE_TYPE_D3D11 : { MoteurD3D11CreateInfo EngineCI ; # if ENGINE_DLL// Charger la dll et importer GetEngineFactoryD3D11() functionauto* GetEngineFactoryD3D11 = LoadGraphicsEngineD3D11(); # endifauto* pFactoryD3D11 = GetEngineFactoryD3D11(); pFactoryD3D11->CreateDeviceAndContextsD3D11(EngineCI, &m_pDevice, &m_pImmediateContext); Fenêtre Win32NativeWindow{hWnd} ; pFactoryD3D11->CreateSwapChainD3D11(m_pDevice, m_pImmediateContext, SCDesc, FullScreenModeDesc{}, Fenêtre, &m_pSwapChain); }pause;cas RENDER_DEVICE_TYPE_D3D12 : { # if ENGINE_DLL// Charger la dll et importer GetEngineFactoryD3D12() functionauto GetEngineFactoryD3D12 = LoadGraphicsEngineD3D12(); # endifEngineD3D12CreateInfo EngineCI;auto* pFactoryD3D12 = GetEngineFactoryD3D12(); pFactoryD3D12->CreateDeviceAndContextsD3D12(EngineCI, &m_pDevice, &m_pImmediateContext); Fenêtre Win32NativeWindow{hWnd} ; pFactoryD3D12->CreateSwapChainD3D12(m_pDevice, m_pImmediateContext, SCDesc, FullScreenModeDesc{}, Fenêtre, &m_pSwapChain); }pause;cas RENDER_DEVICE_TYPE_GL : { # if EXPLICITLY_LOAD_ENGINE_GL_DLL// Charger la dll et importer GetEngineFactoryOpenGL() functionauto GetEngineFactoryOpenGL = LoadGraphicsEngineOpenGL(); # endifauto* pFactoryOpenGL = GetEngineFactoryOpenGL(); MoteurGLCreateInfo EngineCI ; EngineCI.Window.hWnd = hWnd; pFactoryOpenGL->CreateDeviceAndSwapChainGL(EngineCI, &m_pDevice, &m_pImmediateContext, SCDesc, &m_pSwapChain); }pause;cas RENDER_DEVICE_TYPE_VULKAN : { # if EXPLICITLY_LOAD_ENGINE_VK_DLL// Charger la dll et importer GetEngineFactoryVk() functionauto GetEngineFactoryVk = LoadGraphicsEngineVk(); # endifEngineVkCreateInfo EngineCI;auto* pFactoryVk = GetEngineFactoryVk(); pFactoryVk->CreateDeviceAndContextsVk(EngineCI, &m_pDevice, &m_pImmediateContext); Fenêtre Win32NativeWindow{hWnd} ; pFactoryVk->CreateSwapChainVk (m_pDevice, m_pImmediateContext, SCDesc, Window, &m_pSwapChain); }break;par défaut : std::cerr << "Type de périphérique inconnu" ; } }
Sous Windows, le moteur peut être lié statiquement à l'application ou construit en tant que DLL distincte. Dans le premier cas, les fonctions d'usine GetEngineFactoryOpenGL()
, GetEngineFactoryD3D11()
, GetEngineFactoryD3D12()
et GetEngineFactoryVk()
peuvent être appelées directement. Dans le second cas, vous devez charger la DLL dans l'espace d'adressage du processus à l'aide de la fonction LoadGraphicsEngineOpenGL()
, LoadGraphicsEngineD3D11()
, LoadGraphicsEngineD3D12()
ou LoadGraphicsEngineVk()
. Chaque fonction charge la bibliothèque dynamique appropriée et importe les fonctions requises pour initialiser le moteur. Vous devez inclure les en-têtes suivants :
#include "EngineFactoryD3D11.h"#include "EngineFactoryD3D12.h"#include "EngineFactoryOpenGL.h"#include "EngineFactoryVk.h"
Vous devez également ajouter les répertoires suivants aux chemins de recherche d'inclusion :
DiligentCore/Graphics/GraphicsEngineD3D11/interface
DiligentCore/Graphics/GraphicsEngineD3D12/interface
DiligentCore/Graphics/GraphicsEngineOpenGL/interface
DiligentCore/Graphics/GraphicsEngineVulkan/interface
Comme alternative, vous pouvez uniquement ajouter le chemin d'accès au dossier racine, puis utiliser les chemins d'inclusion relatifs à celui-ci.
Activer l'espace de noms Diligent
:
en utilisant l'espace de noms Diligent ;
Les fonctions IEngineFactoryD3D11::CreateDeviceAndContextsD3D11()
, IEngineFactoryD3D12::CreateDeviceAndContextsD3D12()
et IEngineFactoryVk::CreateDeviceAndContextsVk()
peuvent également créer un nombre spécifié de contextes immédiats et différés, qui peuvent être utilisés pour le rendu asynchrone et multithread. enregistrement des commandes. Les contextes ne peuvent être créés que lors de l'initialisation du moteur. La fonction remplit un tableau de pointeurs vers les contextes, où les contextes immédiats vont en premier, suivis de tous les contextes différés.
Pour plus de détails, jetez un œil au fichier Tutorial00_HelloWin32.cpp.
Sur la plateforme Windows universelle, vous pouvez créer un périphérique Direct3D11 ou Direct3D12. L'initialisation s'effectue de la même manière que sur la plateforme Win32. La différence est que vous créez d’abord le périphérique de rendu et les contextes de périphérique en appelant IEngineFactoryD3D11::CreateDeviceAndContextsD3D11()
ou IEngineFactoryD3D12::CreateDeviceAndContextsD3D12()
. La chaîne d'échange est créée ultérieurement par un appel à IEngineFactoryD3D11::CreateSwapChainD3D11()
ou IEngineFactoryD3D12::CreateSwapChainD3D12()
. Veuillez consulter le fichier SampleAppUWP.cpp pour plus de détails.
Sur la plateforme Linux, le moteur prend en charge les backends OpenGL et Vulkan. L'initialisation du contexte GL sous Linux est étroitement liée à la création de fenêtres. Par conséquent, Diligent Engine n'initialise pas le contexte, mais s'attache à celui initialisé par l'application. Un exemple d'initialisation du moteur sous Linux peut être trouvé dans Tutorial00_HelloLinux.cpp.
Sur MacOS, Diligent Engine prend en charge les backends OpenGL, Vulkan et Metal. L'initialisation du contexte GL sur MacOS est effectuée par l'application et le moteur s'attache au contexte créé par l'application ; voir GLView.mm pour plus de détails. Le backend Vulkan est initialisé de la même manière que les autres plates-formes. Voir MetalView.mm.
Sur Android, vous pouvez créer un appareil OpenGLES ou Vulkan. L'extrait de code suivant montre un exemple :
auto* pFactoryOpenGL = GetEngineFactoryOpenGL(); MoteurGLCreateInfo EngineCI ; EngineCI.Window.pAWindow = NativeWindowHandle ; pFactoryOpenGL->CreateDeviceAndSwapChainGL( EngineCI, &m_pDevice, &m_pContext, SCDesc, &m_pSwapChain);
Si le moteur est construit en tant que bibliothèque dynamique, la bibliothèque doit être chargée par l'activité native. Le code suivant montre une méthode possible :
static{try{System.loadLibrary("GraphicsEngineOpenGL"); } catch (UnsatisfiedLinkError e) {Log.e("native-activity", "Échec du chargement de la bibliothèque GraphicsEngineOpenGL.n" + e); } }
L'implémentation iOS prend en charge le backend OpenGLES, Vulkan et Metal. L'initialisation du contexte GL sur iOS est effectuée par l'application, et le moteur s'attache au contexte initialisé par l'application ; voir EAGLView.mm pour plus de détails.
Sur Emscripten, vous pouvez créer un appareil OpenGLES. L'extrait de code suivant montre un exemple :
//Vous devez transmettre l'identifiant du canevas à NativeWindowauto* pFactoryOpenGL = GetEngineFactoryOpenGL(); EngineGLCreateInfo EngineCI = {} ; EngineCI.Window = NativeWindow{"#canvas"} ; pFactoryOpenGL->CreateDeviceAndSwapChainGL (EngineCI, &m_pDevice, &m_pContext, SCDesc, &m_pSwapChain);
Si vous utilisez SDL ou GLFW avec un contexte existant, vous pouvez fournir null comme handle de fenêtre natif : EngineCI.Window = NativeWindow{nullptr}
Le moteur effectue un comptage automatique des références et s'arrête lorsque la dernière référence à un objet moteur est libérée.
Les ressources du périphérique sont créées par le périphérique de rendu. Les deux principaux types de ressources sont les tampons, qui représentent la mémoire linéaire, et les textures, qui utilisent des configurations de mémoire optimisées pour un filtrage rapide. Pour créer un tampon, vous devez remplir la structure BufferDesc
et appeler IRenderDevice::CreateBuffer()
. Le code suivant crée un tampon uniforme (constant) :
BufferDesc BuffDesc; BuffDesc.Name = "Tampon 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);
De même, pour créer une texture, remplissez la structure TextureDesc
et appelez IRenderDevice::CreateTexture()
comme dans l'exemple suivant :
TextureDescTexDesc; TexDesc.Name = "Ma texture 2D"; TexDesc.Type = TEXTURE_TYPE_2D; TexDesc.Width = 1024 ; TexDesc.Hauteur = 1024 ; TexDesc.Format = TEX_FORMAT_RGBA8_UNORM; TexDesc.Usage = USAGE_DEFAULT ; TexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET | BIND_UNORDERED_ACCESS ; TexDesc.Name = "Exemple de texture 2D"; m_pRenderDevice->CreateTexture(TexDesc, nullptr, &m_pTestTex);
Il n’existe qu’une seule fonction CreateTexture()
capable de créer tous types de textures. Le type, le format, la taille du tableau et tous les autres paramètres sont spécifiés par les membres de la structure TextureDesc
.
Pour chaque indicateur de liaison spécifié lors de la création de la texture, l'objet texture crée une vue par défaut. La vue des ressources de shader par défaut traite de la texture entière, la cible de rendu par défaut et les vues de pochoir de profondeur font référence à toutes les tranches du tableau dans le niveau MIP le plus détaillé, et la vue d'accès non ordonné fait référence à la texture entière. Pour obtenir une vue par défaut de la texture, utilisez la fonction ITexture::GetDefaultView()
. Notez que cette fonction n'incrémente pas le compteur de référence de l'interface renvoyée. Vous pouvez créer des vues de texture supplémentaires en utilisant ITexture::CreateView()
. Utilisez IBuffer::CreateView()
pour créer des vues supplémentaires d'un tampon.
Pour créer un shader, remplissez la structure ShaderCreateInfo
:
ShaderCreateInfo ShaderCI ;
Il existe trois façons de créer un shader. La première méthode consiste à fournir un pointeur vers le code source du shader via le membre ShaderCreateInfo::Source
. La deuxième façon consiste à fournir un nom de fichier. La troisième méthode consiste à fournir un pointeur vers le code d'octet compilé via le membre ShaderCreateInfo::ByteCode
. Le moteur graphique est entièrement découplé de la plateforme. Étant donné que le système de fichiers hôte dépend de la plate-forme, la structure expose le membre ShaderCreateInfo::pShaderSourceStreamFactory
qui est destiné à donner au moteur l'accès au système de fichiers. Si vous avez fourni le nom du fichier source, vous devez également fournir un pointeur non nul vers la fabrique de flux source du shader. Si la source du shader contient des directives #include
, la fabrique de flux source sera également utilisée pour charger ces fichiers. Le moteur fournit une implémentation par défaut pour chaque plate-forme prise en charge, ce qui devrait suffire dans la plupart des cas. Vous pouvez cependant définir votre propre implémentation.
Un membre important est ShaderCreateInfo::SourceLanguage
. Les valeurs suivantes sont valides pour ce membre :
SHADER_SOURCE_LANGUAGE_DEFAULT
- Le format source du shader correspond à l'API graphique sous-jacente : HLSL pour le mode D3D11 ou D3D12 et GLSL pour les modes OpenGL, OpenGLES et Vulkan.
SHADER_SOURCE_LANGUAGE_HLSL
- La source du shader est en HLSL. Pour les modes OpenGL et OpenGLES, le code source sera converti en GLSL. Dans le back-end de Vulkan, le code sera compilé directement dans SPIRV.
SHADER_SOURCE_LANGUAGE_GLSL
- La source du shader est en GLSL.
SHADER_SOURCE_LANGUAGE_GLSL_VERBATIM
- Le langage source du shader est GLSL et doit être compilé textuellement.
SHADER_SOURCE_LANGUAGE_MSL
- Le langage source est Metal Shading Language.
Les autres membres de la structure ShaderCreateInfo
définissent le shader, notamment les répertoires de recherche, les définitions de macros de shader, le point d'entrée du shader et d'autres paramètres.
Macros ShaderMacroHelper ; Macros.AddShaderMacro("USE_SHADOWS", 1); Macros.AddShaderMacro("NUM_SHADOW_SAMPLES", 4); Macros.Finalize(); ShaderCI.Macros = Macros ;
Lorsque tout est prêt, appelez IRenderDevice::CreateShader()
pour créer l'objet shader :
ShaderCreateInfo ShaderCI ; ShaderCI.Desc.Name = "MonPixelShader" ; ShaderCI.FilePath = "MonShaderFile.fx"; ShaderCI.EntryPoint = "MonPixelShader"; 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);
Diligent Engine suit le style Direct3D12/Vulkan pour configurer le pipeline graphique/calcul. Un objet d'état de pipelines (PSO) monolithique englobe tous les états requis (toutes les étapes de shader, description de la disposition d'entrée, pochoir de profondeur, description des états de rastériseur et de fusion, etc.). Pour créer un objet d'état de pipeline graphique, définissez une instance de la structure GraphicsPipelineStateCreateInfo
:
GraphicsPipelineStateCreateInfo PSOCreateInfo ; PipelineStateDesc& PSODesc = PSOCreateInfo.PSODesc; PSODesc.Name = "État de mon pipeline" ;
Décrivez les spécificités du pipeline telles que le nombre et le format des cibles de rendu ainsi que le format du pochoir de profondeur :
// Il s'agit d'un pipeline graphiquePSODesc.PipelineType = PIPELINE_TYPE_GRAPHICS; PSOCreateInfo.GraphicsPipeline.NumRenderTargets = 1 ; PSOCreateInfo.GraphicsPipeline.RTVFormats[0] = TEX_FORMAT_RGBA8_UNORM_SRGB; PSOCreateInfo.GraphicsPipeline.DSVFormat = TEX_FORMAT_D32_FLOAT ;
Initialiser la description de l'état du pochoir de profondeur DepthStencilStateDesc
. Notez que le constructeur initialise les membres avec des valeurs par défaut et vous ne pouvez définir que celles qui sont différentes de celles par défaut.
// Initialisation du pochoir de profondeur stateDepthStencilStateDesc& DepthStencilDesc = PSOCreateInfo.GraphicsPipeline.DepthStencilDesc; DepthStencilDesc.DepthEnable = true ; DepthStencilDesc.DepthWriteEnable = true ;
Initialiser la description de l'état de mélange BlendStateDesc
:
// Init blend stateBlendStateDesc& BSDesc = PSOCreateInfo.GraphicsPipeline.BlendDesc; BSDesc.IndependentBlendEnable = False;auto &RT0 = BSDesc.RenderTargets[0]; RT0.BlendEnable = True ; 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 ;
Initialiser la description de l'état du rastériseur RasterizerStateDesc
:
// Initialisation du rastériseur stateRasterizerStateDesc& RasterizerDesc = PSOCreateInfo.GraphicsPipeline.RasterizerDesc; RasterizerDesc.FillMode = FILL_MODE_SOLID ; RasterizerDesc.CullMode = CULL_MODE_NONE ; RasterizerDesc.FrontCounterClockwise = True ; RasterizerDesc.ScissorEnable = True ; RasterizerDesc.AntialiasedLineEnable = False ;
Initialiser la description de la disposition d'entrée InputLayoutDesc
:
// Définir la disposition des entréesInputLayoutDesc& 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);
Définissez la topologie primitive et définissez les pointeurs de shader :
// Définir le shader et la topologie primitivePSOCreateInfo.GraphicsPipeline.PrimitiveTopology = PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; PSOCreateInfo.pVS = m_pVS ; PSOCreateInfo.pPS = m_pPS ;
La disposition des ressources du pipeline indique au moteur comment l'application va utiliser différentes variables de ressources de shader. Pour permettre le regroupement des ressources en fonction de la fréquence attendue des modifications des liaisons de ressources, Diligent Engine introduit la classification des variables de shader :
Les variables statiques ( SHADER_RESOURCE_VARIABLE_TYPE_STATIC
) sont des variables qui ne devraient être définies qu'une seule fois. Ils ne peuvent pas être modifiés une fois qu'une ressource est liée à la variable. De telles variables sont destinées à contenir des constantes globales telles que des tampons constants d'attributs de caméra ou d'attributs d'éclairage globaux. Notez que c'est la liaison de ressource qui ne peut pas changer, tandis que le contenu de la ressource peut changer en fonction de son utilisation.
Les variables mutables ( SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE
) définissent les ressources qui devraient changer selon une fréquence par matériau. Les exemples peuvent inclure des textures diffuses, des cartes normales, etc. Encore une fois, les mises à jour du contenu de la ressource sont orthogonales aux modifications de liaison.
Les variables dynamiques ( SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC
) devraient changer fréquemment et de manière aléatoire.
Pour définir des types de variables, préparez un tableau de structures ShaderResourceVariableDesc
et initialisez les membres PSODesc.ResourceLayout.Variables
et PSODesc.ResourceLayout.NumVariables
. PSODesc.ResourceLayout.DefaultVariableType
peut également être utilisé pour définir le type qui sera utilisé si aucun nom de variable n'est fourni.
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;
Lors de la création d’un état de pipeline, les textures peuvent se voir attribuer de manière permanente des échantillonneurs immuables. Si un échantillonneur immuable est assigné à une texture, il sera toujours utilisé à la place de celui initialisé dans la vue des ressources du texture shader. Pour définir des échantillonneurs immuables, préparez un tableau de structures ImmutableSamplerDesc
et initialisez les membres PSODesc.ResourceLayout.ImmutableSamplers
et PSODesc.ResourceLayout.NumImmutableSamplers
. Notez que les échantillonneurs immuables peuvent être affectés à une variable de texture de n'importe quel type, pas nécessairement statique, de sorte que la liaison de texture puisse être modifiée au moment de l'exécution, tandis que l'échantillonneur restera immuable. Il est fortement recommandé d'utiliser des échantillonneurs immuables autant que possible.
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;
Ce document fournit des informations détaillées sur l'utilisation des échantillonneurs de texture.
Lorsque tous les champs obligatoires de la structure de description PSO sont définis, appelez IRenderDevice::CreateGraphicsPipelineState()
pour créer l'objet PSO :
m_pDevice->CreateGraphicsPipelineState(PSOCreateInfo, &m_pPSO);
Comme mentionné ci-dessus, la liaison des ressources de shader dans Diligent Engine est basée sur le regroupement de variables en 3 groupes différents (statiques, mutables et dynamiques). Les variables statiques sont des variables qui ne devraient être définies qu'une seule fois. Ils ne peuvent pas être modifiés une fois qu'une ressource est liée à la variable. De telles variables sont destinées à contenir des constantes globales telles que des tampons constants d'attributs de caméra ou d'attributs d'éclairage globaux. Ils sont directement liés à l'objet Pipeline State :
m_pPSO->GetStaticShaderVariable(SHADER_TYPE_PIXEL, "g_tex2DShadowMap")->Set(pShadowMapSRV);
Les variables mutables et dynamiques sont liées via un nouvel objet appelé Shader Resource Binding (SRB), qui est créé par l'état du pipeline ( IPipelineState::CreateShaderResourceBinding()
), ou la signature des ressources du pipeline dans les cas d'utilisation avancés :
m_pPSO->CreateShaderResourceBinding(&m_pSRB, true);
Le deuxième paramètre indique au système d'initialiser les structures internes de l'objet SRB qui font référence à des variables statiques dans le PSO.
Les ressources dynamiques et mutables sont ensuite liées via l'objet SRB :
m_pSRB->GetVariable(SHADER_TYPE_PIXEL, "tex2DDiffuse")->Set(pDiffuseTexSRV); m_pSRB->GetVariable(SHADER_TYPE_VERTEX, "cbRandomAttribs")->Set(pRandomAttrsCB);
La différence entre les ressources mutables et dynamiques est que les ressources mutables ne peuvent être définies qu'une seule fois par instance de liaison de ressource de shader. Les ressources dynamiques peuvent être définies plusieurs fois. Il est important de définir correctement le type de variable car cela affecte les performances. Les variables statiques et mutables sont plus efficaces. Les variables dynamiques sont plus coûteuses et introduisent une certaine surcharge d'exécution.
Une autre façon de lier les ressources du shader consiste à créer une interface IResourceMapping
qui mappe les noms littéraux des ressources aux ressources réelles :
Entrées ResourceMappingEntry[] = { {"g_Texture", pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)} } ; ResourceMappingCreateInfo ResMappingCI ; ResMappingCI.pEntries = Entrées ; ResMappingCI.NumEntries = _countof(Entries); RefCntAutoPtr<IResourceMapping> pResMapping; pRenderDevice->CreateResourceMapping(ResMappingCI, &pResMapping);
Le mappage des ressources peut ensuite être utilisé pour lier toutes les ressources statiques dans un état de pipeline ( IPipelineState::BindStaticResources()
) :
m_pPSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
ou toutes les ressources mutables et dynamiques dans une liaison de ressources de shader ( IShaderResourceBinding::BindResources()
) :
m_pSRB->BindResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
Le dernier paramètre de toutes les fonctions BindResources()
définit comment les ressources doivent être résolues :
BIND_SHADER_RESOURCES_UPDATE_STATIC
- Indique que les liaisons de variables statiques doivent être mises à jour.
BIND_SHADER_RESOURCES_UPDATE_MUTABLE
- Indique que les liaisons de variables mutables doivent être mises à jour.
BIND_SHADER_RESOURCES_UPDATE_DYNAMIC
-Indique que les liaisons de variables dynamiques doivent être mises à jour.
BIND_SHADER_RESOURCES_UPDATE_ALL
- Indique que tous les types de variables (statiques, mutables et dynamiques) doivent être mis à jour. Notez que si aucun des indicateurs BIND_SHADER_RESOURCES_UPDATE_STATIC
, BIND_SHADER_RESOURCES_UPDATE_MUTABLE
et BIND_SHADER_RESOURCES_UPDATE_DYNAMIC
n'est défini, tous les types de variables sont mis à jour comme si BIND_SHADER_RESOURCES_UPDATE_ALL
était spécifié.
BIND_SHADER_RESOURCES_KEEP_EXISTING
- Si cet indicateur est spécifié, seules les liaisons non résolues seront mises à jour. Toutes les liaisons existantes conserveront leurs valeurs d'origine. Si cet indicateur n'est pas spécifié, chaque variable de shader sera mise à jour si le mappage contient la ressource correspondante.
BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED
- Si cet indicateur est spécifié, toutes les liaisons de shader devraient être résolues après l'appel. Si ce n'est pas le cas, une erreur sera signalée.
BindResources()
peut être appelée plusieurs fois avec différents mappages de ressources pour lier les ressources. Cependant, il est recommandé d'utiliser un mappage de ressources de grande taille, car la taille du mappage n'affecte pas le temps de recherche des éléments.
Le moteur effectue des contrôles d'exécution pour vérifier que les ressources correctes sont liées. Par exemple, si vous essayez de lier un tampon constant à une variable d'affichage des ressources du shader, une erreur sera affichée sur la console de débogage.
Avant qu'une commande draw puisse être invoquée, tous les tampons de sommets et d'index requis ainsi que l'état du pipeline doivent être liés au contexte du périphérique :
// Définissez les cibles de rendu avant d'émettre une commande de dessin.auto* pRTV = m_pSwapChain->GetCurrentBackBufferRTV();auto* pDSV = m_pSwapChain->GetDepthBufferDSV(); m_pContext->SetRenderTargets(1, &pRTV, pDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);// Effacer la cible de rendu et le float de profondeur-stencilconst zéro[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);// Définir les tampons de sommet et d'indexIBuffer* buffer[] = {m_pVertexBuffer}; Décalages Uint32[] = {0} ; m_pContext->SetVertexBuffers(0, 1, tampon, décalages, 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);
Toutes les méthodes qui peuvent avoir besoin d'effectuer des transitions d'état de ressource prennent l'énumération RESOURCE_STATE_TRANSITION_MODE
comme paramètre. L'énumération définit les modes suivants :
RESOURCE_STATE_TRANSITION_MODE_NONE
- N'effectuez aucune transition d'état de ressource.
RESOURCE_STATE_TRANSITION_MODE_TRANSITION
- Transition des ressources vers les états requis par la commande.
RESOURCE_STATE_TRANSITION_MODE_VERIFY
- N'effectuez pas de transition, mais vérifiez que les états sont corrects.
La dernière étape consiste à engager les ressources du shader dans le contexte du périphérique. Ceci est accompli par la méthode IDeviceContext::CommitShaderResources()
:
m_pContext->CommitShaderResources(m_pSRB, COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES);
Si la méthode n'est pas appelée, le moteur détectera que les ressources ne sont pas validées et affichera un message de débogage. Notez que le dernier paramètre indique au système de faire passer les ressources aux états corrects. Si cet indicateur n'est pas spécifié, les ressources doivent être explicitement transitionnées vers les états requis par un appel à IDeviceContext::TransitionShaderResources()
:
m_pContext->TransitionShaderResources(m_pPSO, m_pSRB);
Notez que la méthode nécessite un pointeur vers l’état du pipeline qui a créé la liaison de ressource de shader.
Lorsque tous les états et ressources requis sont liés, IDeviceContext::DrawIndexed()
peut être utilisé pour exécuter une commande de dessin ou IDeviceContext::DispatchCompute()
peut être utilisé pour exécuter une commande de calcul. Notez que pour une commande draw, un pipeline graphique doit être lié, et pour une commande dispatch, un pipeline de calcul doit être lié. DrawIndexed()
prend la structure DrawIndexedAttribs
comme argument, par exemple :
DrawIndexedAttribs attrs ; attrs.IndexType = VT_UINT16; attrs.NumIndices = 36 ; attrs.Flags = DRAW_FLAG_VERIFY_STATES ; pContext->DrawIndexed(attrs);
L'indicateur DRAW_FLAG_VERIFY_STATES
demande au moteur de vérifier que les tampons de sommets et d'index utilisés par la commande draw sont transférés vers les états appropriés.
DispatchCompute()
prend la structure DispatchComputeAttribs
qui définit les dimensions de la grille de calcul :
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);
Vous pouvez en savoir plus sur l'API du moteur en étudiant des exemples et des didacticiels.
Diligent Engine prend largement en charge l'interopérabilité avec les API de bas niveau sous-jacentes. Le moteur peut être initialisé en s'attachant au périphérique D3D11/D3D12 existant ou au contexte OpenGL/GLES et donne accès aux objets API natifs sous-jacents. Reportez-vous aux pages suivantes pour plus d'informations :
Interopérabilité Direct3D11
Interopérabilité Direct3D12
Interopérabilité OpenGL/GLES
Interopérabilité Vulkan
Suivez les étapes suivantes pour créer le package NuGet :
Installez les packages Python requis
python -m pip install -r ./BuildTools/.NET/requirements.txt
Exécutez le script de génération du package NuGet, par exemple :
python ./BuildTools/.NET/dotnet-build-package.py -c Debug -d ./
Argument | Description | Requis |
---|---|---|
-c ( configuration ) | Configuration de construction de bibliothèques dynamiques natives (par exemple Debug, Release, etc.) | Oui |
-d ( root-dir ) | Le chemin d'accès au répertoire racine de DiligentCore | Oui |
-s ( settings ) | Le chemin d'accès au fichier de paramètres | Non |
dotnet-tests | Indicateur indiquant s'il faut exécuter des tests .NET | Non |
dotnet-publish | Indicateur indiquant s’il faut publier le package sur NuGet Gallery | Non |
free-memory | Utilisez cet argument si vous rencontrez une mémoire insuffisante pendant le processus de construction | Non |
Vous pouvez remplacer les paramètres par défaut à l'aide d'un fichier de paramètres (vérifiez le dictionnaire default_settings
dans dotnet-build-package.py
)
Voir Licence Apache 2.0.
Ce projet comporte des dépendances tierces, chacune pouvant avoir une licence indépendante :
Vulkan-Headers : fichiers d'en-tête Vulkan et registre API (licence Apache 2.0).
SPIRV-Cross : outils d'analyse et de compilation croisée SPIRV (Licence Apache 2.0).
SPIRV-Headers : fichiers d'en-tête SPIRV (licence de type Khronos MIT).
SPIRV-Tools : Outils d'optimisation et de validation SPIRV (Licence Apache 2.0).
glslang : compilateur et validateur de référence Khronos pour GLSL, ESSL et HLSL (licence BSD à 3 clauses, licence BSD à 2 clauses, MIT, licence Apache 2.0).
glew : OpenGL Extension Wrangler Library (bibliothèque graphique Mesa 3-D, licence de type Khronos MIT).
volk : Metaloader pour l'API Vulkan (licence de type Arseny Kapoulkine MIT).
stb : bibliothèques stb du domaine public à fichier unique pour C/C++ (licence MIT ou domaine public).
googletest : Google Testing and Mocking Framework (licence BSD 3 clauses "nouvelle" ou "révisée").
DirectXShaderCompiler : compilateur DirectX Shader basé sur LLVM/Clang (licence de version LLVM).
DXBCChecksum : algorithme de calcul de somme de contrôle DXBC par l'équipe AMD Developer Tools (MIT Lincesne).
xxHash : algorithme de hachage non cryptographique extrêmement rapide (licence BSD à 2 clauses).
Pour contribuer votre code, soumettez une Pull Request à ce référentiel. Diligent Engine est distribué sous la licence Apache 2.0 qui garantit que le contenu du référentiel DiligentCore est exempt de toute charge de propriété intellectuelle. En soumettant du contenu à ce référentiel, vous accordez une licence pour ce contenu selon les mêmes conditions, et vous acceptez que le contenu est exempt de toute réclamation en matière de propriété intellectuelle et que vous avez le droit d'en accorder une licence selon ces conditions.
Diligent Engine utilise le format clang pour garantir un style de code source cohérent dans toute la base de code. Le format est validé par CI pour chaque demande de validation et d'extraction, et la construction échouera si un problème de formatage du code est détecté. Veuillez vous référer à cette page pour obtenir des instructions sur la configuration du format clang et du formatage automatique du code.
Voir l'historique des versions
diligentgraphics.com