Scale.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. // Copyright (C) 2009-2022, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Renderer/Scale.h>
  6. #include <AnKi/Renderer/Renderer.h>
  7. #include <AnKi/Renderer/TemporalAA.h>
  8. #include <AnKi/Core/ConfigSet.h>
  9. #include <AnKi/Renderer/LightShading.h>
  10. #include <AnKi/Renderer/MotionVectors.h>
  11. #include <AnKi/Renderer/GBuffer.h>
  12. #include <AnKi/Renderer/Tonemapping.h>
  13. #if ANKI_COMPILER_GCC_COMPATIBLE
  14. # pragma GCC diagnostic push
  15. # pragma GCC diagnostic ignored "-Wunused-function"
  16. # pragma GCC diagnostic ignored "-Wignored-qualifiers"
  17. #elif ANKI_COMPILER_MSVC
  18. # pragma warning(push)
  19. # pragma warning(disable : 4505)
  20. #endif
  21. #define A_CPU
  22. #include <ThirdParty/FidelityFX/ffx_a.h>
  23. #include <ThirdParty/FidelityFX/ffx_fsr1.h>
  24. #if ANKI_COMPILER_GCC_COMPATIBLE
  25. # pragma GCC diagnostic pop
  26. #elif ANKI_COMPILER_MSVC
  27. # pragma warning(pop)
  28. #endif
  29. namespace anki {
  30. Scale::~Scale()
  31. {
  32. }
  33. Error Scale::init()
  34. {
  35. const Bool needsScaling = m_r->getPostProcessResolution() != m_r->getInternalResolution();
  36. const Bool needsSharpening = getConfig().getRSharpness() > 0.0f;
  37. if(!needsScaling && !needsSharpening)
  38. {
  39. return Error::NONE;
  40. }
  41. const Bool preferCompute = getConfig().getRPreferCompute();
  42. const U32 dlssQuality = getConfig().getRDlssQuality();
  43. const U32 fsrQuality = getConfig().getRFsrQuality();
  44. if(needsScaling)
  45. {
  46. if(dlssQuality > 0 && getGrManager().getDeviceCapabilities().m_dlss)
  47. {
  48. m_upscalingMethod = UpscalingMethod::GR;
  49. }
  50. else if(fsrQuality > 0)
  51. {
  52. m_upscalingMethod = UpscalingMethod::FSR;
  53. }
  54. else
  55. {
  56. m_upscalingMethod = UpscalingMethod::BILINEAR;
  57. }
  58. }
  59. else
  60. {
  61. m_upscalingMethod = UpscalingMethod::NONE;
  62. }
  63. m_sharpenMethod = (needsSharpening) ? SharpenMethod::RCAS : SharpenMethod::NONE;
  64. m_neeedsTonemapping = (m_upscalingMethod == UpscalingMethod::GR); // Because GR upscaling spits HDR
  65. static const Array<const Char*, U32(UpscalingMethod::COUNT)> upscalingMethodNames = {"none", "bilinear", "FSR 1.0",
  66. "DLSS 2"};
  67. static const Array<const Char*, U32(SharpenMethod::COUNT)> sharpenMethodNames = {"none", "RCAS"};
  68. ANKI_R_LOGV("Initializing upscaling. Upscaling method %s, sharpenning method %s",
  69. upscalingMethodNames[U32(m_upscalingMethod)], sharpenMethodNames[U32(m_sharpenMethod)]);
  70. // Scale programs
  71. if(m_upscalingMethod == UpscalingMethod::BILINEAR)
  72. {
  73. const CString shaderFname =
  74. (preferCompute) ? "ShaderBinaries/BlitCompute.ankiprogbin" : "ShaderBinaries/BlitRaster.ankiprogbin";
  75. ANKI_CHECK(getResourceManager().loadResource(shaderFname, m_scaleProg));
  76. const ShaderProgramResourceVariant* variant;
  77. m_scaleProg->getOrCreateVariant(variant);
  78. m_scaleGrProg = variant->getProgram();
  79. }
  80. else if(m_upscalingMethod == UpscalingMethod::FSR)
  81. {
  82. const CString shaderFname =
  83. (preferCompute) ? "ShaderBinaries/FsrCompute.ankiprogbin" : "ShaderBinaries/FsrRaster.ankiprogbin";
  84. ANKI_CHECK(getResourceManager().loadResource(shaderFname, m_scaleProg));
  85. ShaderProgramResourceVariantInitInfo variantInitInfo(m_scaleProg);
  86. variantInitInfo.addMutation("SHARPEN", 0);
  87. variantInitInfo.addMutation("FSR_QUALITY", fsrQuality - 1);
  88. const ShaderProgramResourceVariant* variant;
  89. m_scaleProg->getOrCreateVariant(variantInitInfo, variant);
  90. m_scaleGrProg = variant->getProgram();
  91. }
  92. else if(m_upscalingMethod == UpscalingMethod::GR)
  93. {
  94. GrUpscalerInitInfo inf;
  95. inf.m_sourceTextureResolution = m_r->getInternalResolution();
  96. inf.m_targetTextureResolution = m_r->getPostProcessResolution();
  97. inf.m_upscalerType = GrUpscalerType::DLSS_2;
  98. inf.m_qualityMode = GrUpscalerQualityMode(dlssQuality - 1);
  99. m_grUpscaler = getGrManager().newGrUpscaler(inf);
  100. }
  101. // Sharpen programs
  102. if(m_sharpenMethod == SharpenMethod::RCAS)
  103. {
  104. ANKI_CHECK(getResourceManager().loadResource((preferCompute) ? "ShaderBinaries/FsrCompute.ankiprogbin"
  105. : "ShaderBinaries/FsrRaster.ankiprogbin",
  106. m_sharpenProg));
  107. ShaderProgramResourceVariantInitInfo variantInitInfo(m_sharpenProg);
  108. variantInitInfo.addMutation("SHARPEN", 1);
  109. variantInitInfo.addMutation("FSR_QUALITY", 0);
  110. const ShaderProgramResourceVariant* variant;
  111. m_sharpenProg->getOrCreateVariant(variantInitInfo, variant);
  112. m_sharpenGrProg = variant->getProgram();
  113. }
  114. // Tonemapping programs
  115. if(m_neeedsTonemapping)
  116. {
  117. ANKI_CHECK(getResourceManager().loadResource((preferCompute) ? "ShaderBinaries/TonemapCompute.ankiprogbin"
  118. : "ShaderBinaries/TonemapRaster.ankiprogbin",
  119. m_tonemapProg));
  120. const ShaderProgramResourceVariant* variant;
  121. m_tonemapProg->getOrCreateVariant(variant);
  122. m_tonemapGrProg = variant->getProgram();
  123. }
  124. // Descriptors
  125. Format format;
  126. if(m_upscalingMethod == UpscalingMethod::GR)
  127. {
  128. format = m_r->getHdrFormat();
  129. }
  130. else if(getGrManager().getDeviceCapabilities().m_unalignedBbpTextureFormats)
  131. {
  132. format = Format::R8G8B8_UNORM;
  133. }
  134. else
  135. {
  136. format = Format::R8G8B8A8_UNORM;
  137. }
  138. m_upscaleAndSharpenRtDescr = m_r->create2DRenderTargetDescription(
  139. m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y(), format, "Scaling");
  140. m_upscaleAndSharpenRtDescr.bake();
  141. if(m_neeedsTonemapping)
  142. {
  143. const Format fmt = (getGrManager().getDeviceCapabilities().m_unalignedBbpTextureFormats)
  144. ? Format::R8G8B8_UNORM
  145. : Format::R8G8B8A8_UNORM;
  146. m_tonemapedRtDescr = m_r->create2DRenderTargetDescription(
  147. m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y(), fmt, "Tonemapped");
  148. m_tonemapedRtDescr.bake();
  149. }
  150. m_fbDescr.m_colorAttachmentCount = 1;
  151. m_fbDescr.bake();
  152. return Error::NONE;
  153. }
  154. void Scale::populateRenderGraph(RenderingContext& ctx)
  155. {
  156. if(m_upscalingMethod == UpscalingMethod::NONE && m_sharpenMethod == SharpenMethod::NONE)
  157. {
  158. m_runCtx.m_upscaledTonemappedRt = m_r->getTemporalAA().getTonemappedRt();
  159. m_runCtx.m_upscaledHdrRt = m_r->getTemporalAA().getHdrRt();
  160. m_runCtx.m_sharpenedRt = m_r->getTemporalAA().getTonemappedRt();
  161. m_runCtx.m_tonemappedRt = m_r->getTemporalAA().getTonemappedRt();
  162. return;
  163. }
  164. RenderGraphDescription& rgraph = ctx.m_renderGraphDescr;
  165. const Bool preferCompute = getConfig().getRPreferCompute();
  166. // Step 1: Upscaling
  167. if(m_upscalingMethod == UpscalingMethod::GR)
  168. {
  169. m_runCtx.m_upscaledHdrRt = rgraph.newRenderTarget(m_upscaleAndSharpenRtDescr);
  170. m_runCtx.m_upscaledTonemappedRt = {};
  171. ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("DLSS");
  172. // DLSS says input textures in sampled state and out as storage image
  173. const TextureUsageBit readUsage = TextureUsageBit::ALL_SAMPLED & TextureUsageBit::ALL_COMPUTE;
  174. const TextureUsageBit writeUsage = TextureUsageBit::ALL_IMAGE & TextureUsageBit::ALL_COMPUTE;
  175. pass.newDependency(RenderPassDependency(m_r->getLightShading().getRt(), readUsage));
  176. pass.newDependency(RenderPassDependency(m_r->getMotionVectors().getMotionVectorsRt(), readUsage));
  177. pass.newDependency(RenderPassDependency(m_r->getGBuffer().getDepthRt(), readUsage,
  178. TextureSubresourceInfo(DepthStencilAspectBit::DEPTH)));
  179. pass.newDependency(RenderPassDependency(m_runCtx.m_upscaledHdrRt, writeUsage));
  180. pass.setWork([this, &ctx](RenderPassWorkContext& rgraphCtx) {
  181. runGrUpscaling(ctx, rgraphCtx);
  182. });
  183. }
  184. else if(m_upscalingMethod == UpscalingMethod::FSR || m_upscalingMethod == UpscalingMethod::BILINEAR)
  185. {
  186. m_runCtx.m_upscaledTonemappedRt = rgraph.newRenderTarget(m_upscaleAndSharpenRtDescr);
  187. m_runCtx.m_upscaledHdrRt = {};
  188. const RenderTargetHandle inRt = m_r->getTemporalAA().getTonemappedRt();
  189. const RenderTargetHandle outRt = m_runCtx.m_upscaledTonemappedRt;
  190. if(preferCompute)
  191. {
  192. ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Scale");
  193. pass.newDependency(RenderPassDependency(inRt, TextureUsageBit::SAMPLED_COMPUTE));
  194. pass.newDependency(RenderPassDependency(outRt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
  195. pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
  196. runFsrOrBilinearScaling(rgraphCtx);
  197. });
  198. }
  199. else
  200. {
  201. GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Scale");
  202. pass.setFramebufferInfo(m_fbDescr, {outRt});
  203. pass.newDependency(RenderPassDependency(inRt, TextureUsageBit::SAMPLED_FRAGMENT));
  204. pass.newDependency(RenderPassDependency(outRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
  205. pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
  206. runFsrOrBilinearScaling(rgraphCtx);
  207. });
  208. }
  209. }
  210. else
  211. {
  212. ANKI_ASSERT(m_upscalingMethod == UpscalingMethod::NONE);
  213. // Pretend that it got scaled
  214. m_runCtx.m_upscaledTonemappedRt = m_r->getTemporalAA().getTonemappedRt();
  215. m_runCtx.m_upscaledHdrRt = m_r->getTemporalAA().getHdrRt();
  216. }
  217. // Step 2: Tonemapping
  218. if(m_neeedsTonemapping)
  219. {
  220. m_runCtx.m_tonemappedRt = rgraph.newRenderTarget(m_tonemapedRtDescr);
  221. const RenderTargetHandle inRt = m_runCtx.m_upscaledHdrRt;
  222. const RenderTargetHandle outRt = m_runCtx.m_tonemappedRt;
  223. if(preferCompute)
  224. {
  225. ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Tonemap");
  226. pass.newDependency(RenderPassDependency(inRt, TextureUsageBit::SAMPLED_COMPUTE));
  227. pass.newDependency(RenderPassDependency(outRt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
  228. pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
  229. runTonemapping(rgraphCtx);
  230. });
  231. }
  232. else
  233. {
  234. GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Sharpen");
  235. pass.setFramebufferInfo(m_fbDescr, {outRt});
  236. pass.newDependency(RenderPassDependency(inRt, TextureUsageBit::SAMPLED_FRAGMENT));
  237. pass.newDependency(RenderPassDependency(outRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
  238. pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
  239. runTonemapping(rgraphCtx);
  240. });
  241. }
  242. }
  243. else
  244. {
  245. m_runCtx.m_tonemappedRt = m_runCtx.m_upscaledTonemappedRt;
  246. }
  247. // Step 3: Sharpenning
  248. if(m_sharpenMethod == SharpenMethod::RCAS)
  249. {
  250. m_runCtx.m_sharpenedRt = rgraph.newRenderTarget(m_upscaleAndSharpenRtDescr);
  251. const RenderTargetHandle inRt = m_runCtx.m_tonemappedRt;
  252. const RenderTargetHandle outRt = m_runCtx.m_sharpenedRt;
  253. if(preferCompute)
  254. {
  255. ComputeRenderPassDescription& pass = ctx.m_renderGraphDescr.newComputeRenderPass("Sharpen");
  256. pass.newDependency(RenderPassDependency(inRt, TextureUsageBit::SAMPLED_COMPUTE));
  257. pass.newDependency(RenderPassDependency(outRt, TextureUsageBit::IMAGE_COMPUTE_WRITE));
  258. pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
  259. runRcasSharpening(rgraphCtx);
  260. });
  261. }
  262. else
  263. {
  264. GraphicsRenderPassDescription& pass = ctx.m_renderGraphDescr.newGraphicsRenderPass("Sharpen");
  265. pass.setFramebufferInfo(m_fbDescr, {outRt});
  266. pass.newDependency(RenderPassDependency(inRt, TextureUsageBit::SAMPLED_FRAGMENT));
  267. pass.newDependency(RenderPassDependency(outRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE));
  268. pass.setWork([this](RenderPassWorkContext& rgraphCtx) {
  269. runRcasSharpening(rgraphCtx);
  270. });
  271. }
  272. }
  273. else
  274. {
  275. ANKI_ASSERT(m_sharpenMethod == SharpenMethod::NONE);
  276. // Pretend that it's sharpened
  277. m_runCtx.m_sharpenedRt = m_runCtx.m_tonemappedRt;
  278. }
  279. }
  280. void Scale::runFsrOrBilinearScaling(RenderPassWorkContext& rgraphCtx)
  281. {
  282. CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
  283. const Bool preferCompute = getConfig().getRPreferCompute();
  284. const RenderTargetHandle inRt = m_r->getTemporalAA().getTonemappedRt();
  285. const RenderTargetHandle outRt = m_runCtx.m_upscaledTonemappedRt;
  286. cmdb->bindShaderProgram(m_scaleGrProg);
  287. cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
  288. rgraphCtx.bindColorTexture(0, 1, inRt);
  289. if(preferCompute)
  290. {
  291. rgraphCtx.bindImage(0, 2, outRt);
  292. }
  293. if(m_upscalingMethod == UpscalingMethod::FSR)
  294. {
  295. class
  296. {
  297. public:
  298. UVec4 m_fsrConsts0;
  299. UVec4 m_fsrConsts1;
  300. UVec4 m_fsrConsts2;
  301. UVec4 m_fsrConsts3;
  302. UVec2 m_viewportSize;
  303. UVec2 m_padding;
  304. } pc;
  305. const Vec2 inRez(m_r->getInternalResolution());
  306. const Vec2 outRez(m_r->getPostProcessResolution());
  307. FsrEasuCon(&pc.m_fsrConsts0[0], &pc.m_fsrConsts1[0], &pc.m_fsrConsts2[0], &pc.m_fsrConsts3[0], inRez.x(),
  308. inRez.y(), inRez.x(), inRez.y(), outRez.x(), outRez.y());
  309. pc.m_viewportSize = m_r->getPostProcessResolution();
  310. cmdb->setPushConstants(&pc, sizeof(pc));
  311. }
  312. else if(preferCompute)
  313. {
  314. class
  315. {
  316. public:
  317. Vec2 m_viewportSize;
  318. UVec2 m_viewportSizeU;
  319. } pc;
  320. pc.m_viewportSize = Vec2(m_r->getPostProcessResolution());
  321. pc.m_viewportSizeU = m_r->getPostProcessResolution();
  322. cmdb->setPushConstants(&pc, sizeof(pc));
  323. }
  324. if(preferCompute)
  325. {
  326. dispatchPPCompute(cmdb, 8, 8, m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y());
  327. }
  328. else
  329. {
  330. cmdb->setViewport(0, 0, m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y());
  331. cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
  332. }
  333. }
  334. void Scale::runRcasSharpening(RenderPassWorkContext& rgraphCtx)
  335. {
  336. CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
  337. const Bool preferCompute = getConfig().getRPreferCompute();
  338. const RenderTargetHandle inRt = m_runCtx.m_tonemappedRt;
  339. const RenderTargetHandle outRt = m_runCtx.m_sharpenedRt;
  340. cmdb->bindShaderProgram(m_sharpenGrProg);
  341. cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearClamp);
  342. rgraphCtx.bindColorTexture(0, 1, inRt);
  343. if(preferCompute)
  344. {
  345. rgraphCtx.bindImage(0, 2, outRt);
  346. }
  347. class
  348. {
  349. public:
  350. UVec4 m_fsrConsts0;
  351. UVec4 m_fsrConsts1;
  352. UVec4 m_fsrConsts2;
  353. UVec4 m_fsrConsts3;
  354. UVec2 m_viewportSize;
  355. UVec2 m_padding;
  356. } pc;
  357. F32 sharpness = getConfig().getRSharpness(); // [0, 1]
  358. sharpness *= 3.0f; // [0, 3]
  359. sharpness = 3.0f - sharpness; // [3, 0], RCAS translates 0 to max sharpness
  360. FsrRcasCon(&pc.m_fsrConsts0[0], sharpness);
  361. pc.m_viewportSize = m_r->getPostProcessResolution();
  362. cmdb->setPushConstants(&pc, sizeof(pc));
  363. if(preferCompute)
  364. {
  365. dispatchPPCompute(cmdb, 8, 8, m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y());
  366. }
  367. else
  368. {
  369. cmdb->setViewport(0, 0, m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y());
  370. cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
  371. }
  372. }
  373. void Scale::runGrUpscaling(RenderingContext& ctx, RenderPassWorkContext& rgraphCtx)
  374. {
  375. const Vec2 srcRes(m_r->getInternalResolution());
  376. const Bool reset = m_r->getFrameCount() == 0;
  377. const Vec2 mvScale = srcRes; // UV space to Pixel space factor
  378. // In [-texSize / 2, texSize / 2] -> sub-pixel space {-0.5, 0.5}
  379. const Vec2 jitterOffset = ctx.m_matrices.m_jitter.getTranslationPart().xy() * srcRes * 0.5f;
  380. CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
  381. TextureViewPtr srcView = rgraphCtx.createTextureView(m_r->getLightShading().getRt());
  382. TextureViewPtr motionVectorsView = rgraphCtx.createTextureView(m_r->getMotionVectors().getMotionVectorsRt());
  383. TextureViewPtr depthView = rgraphCtx.createTextureView(m_r->getGBuffer().getDepthRt());
  384. TextureViewPtr exposureView = rgraphCtx.createTextureView(m_r->getTonemapping().getRt());
  385. TextureViewPtr dstView = rgraphCtx.createTextureView(m_runCtx.m_upscaledHdrRt);
  386. cmdb->upscale(m_grUpscaler, srcView, dstView, motionVectorsView, depthView, exposureView, reset, jitterOffset,
  387. mvScale);
  388. }
  389. void Scale::runTonemapping(RenderPassWorkContext& rgraphCtx)
  390. {
  391. CommandBufferPtr& cmdb = rgraphCtx.m_commandBuffer;
  392. const Bool preferCompute = getConfig().getRPreferCompute();
  393. const RenderTargetHandle inRt = m_runCtx.m_upscaledHdrRt;
  394. const RenderTargetHandle outRt = m_runCtx.m_tonemappedRt;
  395. cmdb->bindShaderProgram(m_tonemapGrProg);
  396. cmdb->bindSampler(0, 0, m_r->getSamplers().m_nearestNearestClamp);
  397. rgraphCtx.bindColorTexture(0, 1, inRt);
  398. rgraphCtx.bindImage(0, 2, m_r->getTonemapping().getRt());
  399. class
  400. {
  401. public:
  402. Vec2 m_viewportSizeOverOne;
  403. UVec2 m_viewportSize;
  404. } pc;
  405. pc.m_viewportSizeOverOne = 1.0f / Vec2(m_r->getPostProcessResolution());
  406. pc.m_viewportSize = m_r->getPostProcessResolution();
  407. cmdb->setPushConstants(&pc, sizeof(pc));
  408. if(preferCompute)
  409. {
  410. rgraphCtx.bindImage(0, 3, outRt);
  411. dispatchPPCompute(cmdb, 8, 8, m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y());
  412. }
  413. else
  414. {
  415. cmdb->setViewport(0, 0, m_r->getPostProcessResolution().x(), m_r->getPostProcessResolution().y());
  416. cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
  417. }
  418. }
  419. } // end namespace anki