DKGL2 sample codes

ComputeShader.cpp 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. #include <cstddef>
  2. #include "app.h"
  3. #include "util.h"
  4. class UVQuad : public GPUGeometry
  5. {
  6. private:
  7. struct Vertex
  8. {
  9. DKVector3 Pos;
  10. DKVector2 UV;
  11. };
  12. DKArray<UVQuad::Vertex> vertices =
  13. {
  14. { { 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f } },
  15. { { -1.0f, 1.0f, 0.0f }, { 0.0f, 0.0f } },
  16. { { -1.0f, -1.0f, 0.0f }, { 0.0f, 1.0f } },
  17. { { 1.0f, -1.0f, 0.0f }, { 1.0f, 1.0f } }
  18. };
  19. DKArray<uint32_t> indices = { 0,1,2,2,3,0 };
  20. public:
  21. UVQuad() = default;
  22. size_t VerticesCount() const { return vertices.Count(); }
  23. size_t IndicesCount() const { return indices.Count(); }
  24. UVQuad::Vertex* VerticesData() { return vertices; }
  25. uint32_t* IndicesData() { return indices; }
  26. void InitializeGpuResource(DKCommandQueue* queue)
  27. {
  28. DKGraphicsDevice* device = queue->Device();
  29. uint32_t vertexBufferSize = static_cast<uint32_t>(VerticesCount()) * sizeof(UVQuad::Vertex);
  30. uint32_t indexBufferSize = IndicesCount() * sizeof(uint32_t);
  31. vertexBuffer = device->CreateBuffer(vertexBufferSize, DKGpuBuffer::StorageModeShared, DKCpuCacheModeReadWrite);
  32. memcpy(vertexBuffer->Contents(), VerticesData(), vertexBufferSize);
  33. vertexBuffer->Flush();
  34. indexBuffer = device->CreateBuffer(indexBufferSize, DKGpuBuffer::StorageModeShared, DKCpuCacheModeReadWrite);
  35. memcpy(indexBuffer->Contents(), IndicesData(), indexBufferSize);
  36. indexBuffer->Flush();
  37. // setup vertex buffer and attributes
  38. vertexDesc.attributes = {
  39. { DKVertexFormat::Float3, offsetof(UVQuad::Vertex, Pos), 0, 0 },
  40. { DKVertexFormat::Float2, offsetof(UVQuad::Vertex, UV), 0, 1 },
  41. };
  42. vertexDesc.layouts = {
  43. { DKVertexStepRate::Vertex, sizeof(UVQuad::Vertex), 0 },
  44. };
  45. }
  46. };
  47. class GPUShader
  48. {
  49. private:
  50. DKObject<DKData> shaderData = nullptr;
  51. DKObject<DKShaderModule> shaderModule = nullptr;
  52. DKObject<DKShaderFunction> shaderFunc = nullptr;
  53. public:
  54. struct { uint32_t x, y, z; } threadgroupSize;
  55. GPUShader(DKData* data) : shaderData(data), threadgroupSize{1,1,1}
  56. {
  57. }
  58. void InitializeGpuResource(DKCommandQueue* queue)
  59. {
  60. if (shaderData)
  61. {
  62. DKGraphicsDevice* device = queue->Device();
  63. DKShader shader(shaderData);
  64. shaderModule = device->CreateShaderModule(&shader);
  65. shaderFunc = shaderModule->CreateFunction(shaderModule->FunctionNames().Value(0));
  66. if (shaderFunc)
  67. {
  68. threadgroupSize = { shader.ThreadgroupSize().x,
  69. shader.ThreadgroupSize().y,
  70. shader.ThreadgroupSize().z };
  71. }
  72. }
  73. }
  74. DKShaderFunction* Function() { return shaderFunc; }
  75. };
  76. class GraphicShaderBindingSet
  77. {
  78. public:
  79. struct UBO
  80. {
  81. DKMatrix4 projectionMatrix;
  82. DKMatrix4 modelMatrix;
  83. };
  84. private:
  85. DKShaderBindingSetLayout descriptorSetLayout;
  86. DKObject<DKShaderBindingSet> descriptorSetPreCompute;
  87. DKObject<DKShaderBindingSet> descriptorSetPostCompute;
  88. DKObject<DKRenderPipelineState> pipelineState;
  89. DKObject<DKGpuBuffer> uniformBuffer;
  90. UBO* ubo = nullptr;
  91. public:
  92. GraphicShaderBindingSet() = default;
  93. DKShaderBindingSet* PrecomputeDescSet() { return descriptorSetPreCompute; }
  94. DKShaderBindingSet* PostcomputeDescSet() { return descriptorSetPostCompute; }
  95. DKRenderPipelineState* GraphicPipelineState() { return pipelineState; }
  96. void InitializeGpuResource(DKGraphicsDevice* device)
  97. {
  98. if (1)
  99. {
  100. DKShaderBinding bindings[2] = {
  101. {
  102. 0,
  103. DKShader::DescriptorTypeUniformBuffer,
  104. 1,
  105. nullptr
  106. },
  107. {
  108. 1,
  109. DKShader::DescriptorTypeTextureSampler,
  110. 1,
  111. nullptr
  112. },
  113. };
  114. descriptorSetLayout.bindings.Add(bindings, 2);
  115. }
  116. descriptorSetPreCompute = device->CreateShaderBindingSet(descriptorSetLayout);
  117. descriptorSetPostCompute = device->CreateShaderBindingSet(descriptorSetLayout);
  118. uniformBuffer = device->CreateBuffer(sizeof(GraphicShaderBindingSet::UBO), DKGpuBuffer::StorageModeShared, DKCpuCacheModeReadWrite);
  119. if (descriptorSetPreCompute)
  120. {
  121. if (uniformBuffer)
  122. {
  123. ubo = reinterpret_cast<UBO*>(uniformBuffer->Contents());
  124. ubo->projectionMatrix = DKMatrix4::identity;
  125. ubo->modelMatrix = DKMatrix4::identity;
  126. uniformBuffer->Flush();
  127. descriptorSetPreCompute->SetBuffer(0, uniformBuffer, 0, sizeof(UBO));
  128. }
  129. }
  130. if (descriptorSetPostCompute)
  131. {
  132. if (uniformBuffer && ubo)
  133. {
  134. descriptorSetPostCompute->SetBuffer(0, uniformBuffer, 0, sizeof(UBO));
  135. }
  136. }
  137. }
  138. DKGpuBuffer* UniformBuffer() { return uniformBuffer; }
  139. UBO* UniformBufferO() { return ubo; }
  140. };
  141. class ComputeShaderDemo : public SampleApp
  142. {
  143. DKObject<DKWindow> window;
  144. DKObject<DKThread> renderThread;
  145. DKAtomicNumber32 runningRenderThread;
  146. //Resource
  147. DKObject<UVQuad> quad;
  148. DKObject<DKTexture> textureColorMap;
  149. DKObject<DKSamplerState> sampleState = nullptr;;
  150. DKObject<GraphicShaderBindingSet> graphicShaderBindingSet = nullptr;
  151. public:
  152. DKObject<DKTexture> LoadTexture2D(DKCommandQueue* queue, DKData* data)
  153. {
  154. DKObject<DKImage> image = DKImage::Create(data);
  155. if (image)
  156. {
  157. DKGraphicsDevice* device = queue->Device();
  158. DKTextureDescriptor texDesc = {};
  159. texDesc.textureType = DKTexture::Type2D;
  160. texDesc.pixelFormat = DKPixelFormat::RGBA8Unorm;
  161. texDesc.width = image->Width();
  162. texDesc.height = image->Height();
  163. texDesc.depth = 1;
  164. texDesc.mipmapLevels = 1;
  165. texDesc.sampleCount = 1;
  166. texDesc.arrayLength = 1;
  167. texDesc.usage = DKTexture::UsageStorage | DKTexture::UsageShaderRead | DKTexture::UsageCopyDestination | DKTexture::UsageSampled;
  168. DKObject<DKTexture> tex = device->CreateTexture(texDesc);
  169. if (tex)
  170. {
  171. size_t bytesPerPixel = image->BytesPerPixel();
  172. DKASSERT_DESC(bytesPerPixel == DKPixelFormatBytesPerPixel(texDesc.pixelFormat), "BytesPerPixel mismatch!");
  173. uint32_t width = image->Width();
  174. uint32_t height = image->Height();
  175. size_t bufferLength = bytesPerPixel * width * height;
  176. DKObject<DKGpuBuffer> stagingBuffer = device->CreateBuffer(bufferLength, DKGpuBuffer::StorageModeShared, DKCpuCacheModeReadWrite);
  177. memcpy(stagingBuffer->Contents(), image->Contents(), bufferLength);
  178. stagingBuffer->Flush();
  179. DKObject<DKCommandBuffer> cb = queue->CreateCommandBuffer();
  180. DKObject<DKCopyCommandEncoder> encoder = cb->CreateCopyCommandEncoder();
  181. encoder->CopyFromBufferToTexture(stagingBuffer,
  182. { 0, width, height },
  183. tex,
  184. { 0,0, 0,0,0 },
  185. { width,height,1 });
  186. encoder->EndEncoding();
  187. cb->Commit();
  188. DKLog("Texture created!");
  189. return tex;
  190. }
  191. }
  192. return nullptr;
  193. }
  194. void RenderThread(void)
  195. {
  196. // Device and Queue Preperation
  197. DKObject<DKGraphicsDevice> device = DKGraphicsDevice::SharedInstance();
  198. DKObject<DKCommandQueue> graphicsQueue;
  199. DKObject<DKCommandQueue> computeQueue;
  200. bool useSingleQueue = true;
  201. bool useSingleCommandBuffer = true;
  202. if (useSingleQueue)
  203. {
  204. graphicsQueue = device->CreateCommandQueue(DKCommandQueue::Graphics | DKCommandQueue::Compute);
  205. computeQueue = graphicsQueue;
  206. }
  207. else
  208. {
  209. graphicsQueue = device->CreateCommandQueue(DKCommandQueue::Graphics);
  210. computeQueue = device->CreateCommandQueue(DKCommandQueue::Compute);
  211. }
  212. // Geometry Initialzie
  213. quad->InitializeGpuResource(graphicsQueue);
  214. // create shaders
  215. DKObject<DKData> vertData = resourcePool.LoadResourceData("shaders/ComputeShader/texture.vert.spv");
  216. DKObject<DKData> fragData = resourcePool.LoadResourceData("shaders/ComputeShader/texture.frag.spv");
  217. DKObject<DKData> embossData = resourcePool.LoadResourceData("shaders/ComputeShader/emboss.comp.spv");
  218. DKObject<DKData> edgedetectData = resourcePool.LoadResourceData("shaders/ComputeShader/edgedetect.comp.spv");
  219. DKObject<DKData> sharpenData = resourcePool.LoadResourceData("shaders/ComputeShader/sharpen.comp.spv");
  220. DKObject<GPUShader> vs = DKOBJECT_NEW GPUShader(vertData);
  221. DKObject<GPUShader> fs = DKOBJECT_NEW GPUShader(fragData);
  222. DKObject<GPUShader> cs_e = DKOBJECT_NEW GPUShader(embossData);
  223. DKObject<GPUShader> cs_ed = DKOBJECT_NEW GPUShader(edgedetectData);
  224. DKObject<GPUShader> cs_sh = DKOBJECT_NEW GPUShader(sharpenData);
  225. vs->InitializeGpuResource(graphicsQueue);
  226. fs->InitializeGpuResource(graphicsQueue);
  227. cs_e->InitializeGpuResource(computeQueue);
  228. cs_ed->InitializeGpuResource(computeQueue);
  229. cs_sh->InitializeGpuResource(computeQueue);
  230. auto vsf = vs->Function();
  231. auto fsf = fs->Function();
  232. auto cs_ef = cs_e->Function();
  233. auto cs_edf = cs_ed->Function();
  234. auto cs_shf = cs_sh->Function();
  235. // Texture Resource Initialize
  236. DKObject<DKTexture> sourceTexture = LoadTexture2D(graphicsQueue, resourcePool.LoadResourceData("textures/Vulkan.png"));
  237. DKObject<DKTexture> targetTexture = [](DKGraphicsDevice* device, int width, int height) {
  238. DKTextureDescriptor texDesc = {};
  239. texDesc.textureType = DKTexture::Type2D;
  240. texDesc.pixelFormat = DKPixelFormat::BGRA8Unorm;
  241. texDesc.width = width;
  242. texDesc.height = height;
  243. texDesc.depth = 1;
  244. texDesc.mipmapLevels = 1;
  245. texDesc.sampleCount = 1;
  246. texDesc.arrayLength = 1;
  247. texDesc.usage = DKTexture::UsageStorage | // For Compute Shader
  248. DKTexture::UsageSampled; // For FragmentShader
  249. return device->CreateTexture(texDesc);
  250. }(graphicsQueue->Device(), sourceTexture->Width(), sourceTexture->Height());
  251. // create sampler for fragment-shader
  252. DKSamplerDescriptor samplerDesc = {};
  253. samplerDesc.magFilter = DKSamplerDescriptor::MinMagFilterLinear;
  254. samplerDesc.minFilter = DKSamplerDescriptor::MinMagFilterLinear;
  255. samplerDesc.addressModeU = DKSamplerDescriptor::AddressModeClampToEdge;
  256. samplerDesc.addressModeV = DKSamplerDescriptor::AddressModeClampToEdge;
  257. samplerDesc.addressModeW = DKSamplerDescriptor::AddressModeClampToEdge;
  258. samplerDesc.maxAnisotropy = 1;
  259. DKObject<DKSamplerState> sampler = device->CreateSamplerState(samplerDesc);
  260. DKObject<DKSwapChain> swapChain = graphicsQueue->CreateSwapChain(window);
  261. DKLog("VertexFunction.VertexAttributes: %d", vsf->StageInputAttributes().Count());
  262. for (int i = 0; i < vsf->StageInputAttributes().Count(); ++i)
  263. {
  264. const DKShaderAttribute& attr = vsf->StageInputAttributes().Value(i);
  265. DKLog(" --> VertexAttribute[%d]: \"%ls\" (location:%u)", i, (const wchar_t*)attr.name, attr.location);
  266. }
  267. DKRenderPipelineDescriptor pipelineDescriptor = {};
  268. // setup shader
  269. pipelineDescriptor.vertexFunction = vsf;
  270. pipelineDescriptor.fragmentFunction = fsf;
  271. // setup color-attachment render-targets
  272. pipelineDescriptor.colorAttachments.Resize(1);
  273. pipelineDescriptor.colorAttachments.Value(0).pixelFormat = swapChain->ColorPixelFormat();
  274. pipelineDescriptor.colorAttachments.Value(0).blendState.enabled = false;
  275. pipelineDescriptor.colorAttachments.Value(0).blendState.sourceRGBBlendFactor = DKBlendFactor::SourceAlpha;
  276. pipelineDescriptor.colorAttachments.Value(0).blendState.destinationRGBBlendFactor = DKBlendFactor::OneMinusSourceAlpha;
  277. // setup depth-stencil
  278. pipelineDescriptor.depthStencilAttachmentPixelFormat = DKPixelFormat::D32Float;
  279. pipelineDescriptor.depthStencilDescriptor.depthWriteEnabled = true;
  280. pipelineDescriptor.depthStencilDescriptor.depthCompareFunction = DKCompareFunctionLessEqual;
  281. // setup vertex buffer and attributes
  282. pipelineDescriptor.vertexDescriptor = quad->VertexDescriptor();
  283. // setup topology and rasterization
  284. pipelineDescriptor.primitiveTopology = DKPrimitiveType::Triangle;
  285. pipelineDescriptor.frontFace = DKFrontFace::CCW;
  286. pipelineDescriptor.triangleFillMode = DKTriangleFillMode::Fill;
  287. pipelineDescriptor.depthClipMode = DKDepthClipMode::Clip;
  288. pipelineDescriptor.cullMode = DKCullMode::Back;
  289. pipelineDescriptor.rasterizationEnabled = true;
  290. DKPipelineReflection reflection;
  291. DKObject<DKRenderPipelineState> pipelineState = device->CreateRenderPipeline(pipelineDescriptor, &reflection);
  292. if (pipelineState)
  293. {
  294. PrintPipelineReflection(&reflection, DKLogCategory::Verbose);
  295. }
  296. ///
  297. graphicShaderBindingSet = DKOBJECT_NEW GraphicShaderBindingSet();
  298. graphicShaderBindingSet->InitializeGpuResource(device);
  299. auto uboBuffer = graphicShaderBindingSet->UniformBuffer();
  300. auto ubo = graphicShaderBindingSet->UniformBufferO();
  301. // ComputerBuffer Layout
  302. DKShaderBindingSetLayout ComputeLayout;
  303. if (1)
  304. {
  305. DKShaderBinding bindings[2] = {
  306. {
  307. 0,
  308. DKShader::DescriptorTypeStorageTexture,
  309. 1,
  310. nullptr
  311. }, // Input Image (read-only)
  312. {
  313. 1,
  314. DKShader::DescriptorTypeStorageTexture,
  315. 1,
  316. nullptr
  317. }, // Output image (write)
  318. };
  319. ComputeLayout.bindings.Add(bindings, 2);
  320. }
  321. DKObject<DKShaderBindingSet> computebindSet = device->CreateShaderBindingSet(ComputeLayout);
  322. //auto CS_EF = CS_E->Function();
  323. //auto CS_EDF = CS_ED->Function();
  324. //auto CS_SHF = CS_SH->Function();
  325. DKComputePipelineDescriptor embossComputePipelineDescriptor = {};
  326. embossComputePipelineDescriptor.computeFunction = cs_ef;
  327. DKObject<DKComputePipelineState> emboss = device->CreateComputePipeline(embossComputePipelineDescriptor);
  328. DKObject<DKTexture> depthBuffer = nullptr;
  329. DKTimer timer;
  330. timer.Reset();
  331. DKLog("Render thread begin");
  332. while (!runningRenderThread.CompareAndSet(0, 0))
  333. {
  334. DKRenderPassDescriptor rpd = swapChain->CurrentRenderPassDescriptor();
  335. double t = timer.Elapsed();
  336. double waveT = (cos(t) + 1.0) * 0.5;
  337. rpd.colorAttachments.Value(0).clearColor = DKColor(waveT, 0.0, 0.0, 0.0);
  338. int width = rpd.colorAttachments.Value(0).renderTarget->Width();
  339. int height = rpd.colorAttachments.Value(0).renderTarget->Height();
  340. if (depthBuffer)
  341. {
  342. if (depthBuffer->Width() != width ||
  343. depthBuffer->Height() != height )
  344. depthBuffer = nullptr;
  345. }
  346. if (depthBuffer == nullptr)
  347. {
  348. // create depth buffer
  349. DKTextureDescriptor texDesc = {};
  350. texDesc.textureType = DKTexture::Type2D;
  351. texDesc.pixelFormat = DKPixelFormat::D32Float;
  352. texDesc.width = width;
  353. texDesc.height = height;
  354. texDesc.depth = 1;
  355. texDesc.mipmapLevels = 1;
  356. texDesc.sampleCount = 1;
  357. texDesc.arrayLength = 1;
  358. texDesc.usage = DKTexture::UsageRenderTarget;
  359. depthBuffer = device->CreateTexture(texDesc);
  360. }
  361. rpd.depthStencilAttachment.renderTarget = depthBuffer;
  362. rpd.depthStencilAttachment.loadAction = DKRenderPassAttachmentDescriptor::LoadActionClear;
  363. rpd.depthStencilAttachment.storeAction = DKRenderPassAttachmentDescriptor::StoreActionDontCare;
  364. DKObject<DKComputeCommandEncoder> computeEncoder = nullptr;
  365. DKObject<DKRenderCommandEncoder> renderEncoder = nullptr;
  366. if (1)
  367. {
  368. auto commandBuffer = computeQueue->CreateCommandBuffer();
  369. computeEncoder = commandBuffer->CreateComputeCommandEncoder();
  370. }
  371. if (1)
  372. {
  373. DKObject<DKCommandBuffer> commandBuffer = nullptr;
  374. if (computeQueue == graphicsQueue && useSingleCommandBuffer)
  375. commandBuffer = computeEncoder->CommandBuffer();
  376. else
  377. commandBuffer = graphicsQueue->CreateCommandBuffer();
  378. renderEncoder = commandBuffer->CreateRenderCommandEncoder(rpd);
  379. }
  380. if (computeEncoder)
  381. {
  382. if (computebindSet)
  383. {
  384. computebindSet->SetTexture(0, sourceTexture);
  385. computebindSet->SetTexture(1, targetTexture);
  386. }
  387. computeEncoder->SetComputePipelineState(emboss);
  388. computeEncoder->SetResources(0, computebindSet);
  389. computeEncoder->Dispatch(targetTexture->Width() / cs_e->threadgroupSize.x,
  390. targetTexture->Height() / cs_e->threadgroupSize.y,
  391. 1);
  392. computeEncoder->EndEncoding();
  393. }
  394. if (renderEncoder)
  395. {
  396. if (graphicShaderBindingSet->PostcomputeDescSet() && ubo)
  397. {
  398. graphicShaderBindingSet->PostcomputeDescSet()->SetBuffer(0, uboBuffer, 0, sizeof(GraphicShaderBindingSet::UBO));
  399. graphicShaderBindingSet->PostcomputeDescSet()->SetTexture(1, targetTexture);
  400. graphicShaderBindingSet->PostcomputeDescSet()->SetSamplerState(1, sampler);
  401. }
  402. renderEncoder->SetRenderPipelineState(pipelineState);
  403. renderEncoder->SetVertexBuffer(quad->VertexBuffer(), 0, 0);
  404. renderEncoder->SetIndexBuffer(quad->IndexBuffer(), 0, DKIndexType::UInt32);
  405. renderEncoder->SetResources(0, graphicShaderBindingSet->PostcomputeDescSet());
  406. // draw scene!
  407. renderEncoder->DrawIndexed(quad->IndicesCount(), 1, 0, 0, 0);
  408. renderEncoder->EndEncoding();
  409. if (computeEncoder->CommandBuffer() != renderEncoder->CommandBuffer())
  410. computeEncoder->CommandBuffer()->Commit();
  411. renderEncoder->CommandBuffer()->Commit();
  412. swapChain->Present();
  413. }
  414. else
  415. {
  416. }
  417. DKThread::Sleep(0.01);
  418. }
  419. DKLog("RenderThread terminating...");
  420. }
  421. void OnInitialize(void) override
  422. {
  423. SampleApp::OnInitialize();
  424. DKLogD("%s", DKGL_FUNCTION_NAME);
  425. // create window
  426. window = DKWindow::Create("DefaultWindow");
  427. window->SetOrigin({ 0, 0 });
  428. window->Resize({ 512, 512 });
  429. window->Activate();
  430. window->AddEventHandler(this, DKFunction([this](const DKWindow::WindowEvent& e)
  431. {
  432. if (e.type == DKWindow::WindowEvent::WindowClosed)
  433. DKApplication::Instance()->Terminate(0);
  434. }), NULL, NULL);
  435. quad = DKOBJECT_NEW UVQuad();
  436. runningRenderThread = 1;
  437. renderThread = DKThread::Create(DKFunction(this, &ComputeShaderDemo::RenderThread)->Invocation());
  438. }
  439. void OnTerminate(void) override
  440. {
  441. DKLogD("%s", DKGL_FUNCTION_NAME);
  442. runningRenderThread = 0;
  443. renderThread->WaitTerminate();
  444. renderThread = NULL;
  445. window = NULL;
  446. SampleApp::OnTerminate();
  447. }
  448. };
  449. #ifdef _WIN32
  450. int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
  451. _In_opt_ HINSTANCE hPrevInstance,
  452. _In_ LPWSTR lpCmdLine,
  453. _In_ int nCmdShow)
  454. #else
  455. int main(int argc, const char * argv[])
  456. #endif
  457. {
  458. ComputeShaderDemo app;
  459. DKPropertySet::SystemConfig().SetValue("AppDelegate", "AppDelegate");
  460. DKPropertySet::SystemConfig().SetValue("GraphicsAPI", "Vulkan");
  461. return app.Run();
  462. }