diff --git a/examples_tests/23.Autoexposure/main.cpp b/examples_tests/23.Autoexposure/main.cpp index eeefbb4fa..63501fce3 100644 --- a/examples_tests/23.Autoexposure/main.cpp +++ b/examples_tests/23.Autoexposure/main.cpp @@ -3,10 +3,9 @@ #include #include -#include "../../ext/ScreenShot/ScreenShot.h" -#include "../../ext/FullScreenTriangle/FullScreenTriangle.h" + +//#include "../../ext/ScreenShot/ScreenShot.h" #include "../source/Irrlicht/COpenGLDriver.h" -#include "COpenGLStateManager.h" #include "../ext/AutoExposure/CToneMapper.h" #include "../common/QToQuitEventReceiver.h" @@ -15,161 +14,21 @@ using namespace irr; using namespace core; -#define OPENGL_DEBUG - - -#ifdef OPENGL_DEBUG -void APIENTRY openGLCBFunc(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const GLchar* message, const void* userParam) -{ - core::stringc outStr; - switch (severity) - { - //case GL_DEBUG_SEVERITY_HIGH: - case GL_DEBUG_SEVERITY_HIGH_ARB: - outStr = "[H.I.G.H]"; - break; - //case GL_DEBUG_SEVERITY_MEDIUM: - case GL_DEBUG_SEVERITY_MEDIUM_ARB: - outStr = "[MEDIUM]"; - break; - //case GL_DEBUG_SEVERITY_LOW: - case GL_DEBUG_SEVERITY_LOW_ARB: - outStr = "[ LOW ]"; - break; - case GL_DEBUG_SEVERITY_NOTIFICATION: - outStr = "[ LOW ]"; - break; - default: - outStr = "[UNKNOWN]"; - break; - } - switch (source) - { - //case GL_DEBUG_SOURCE_API: - case GL_DEBUG_SOURCE_API_ARB: - switch (type) - { - //case GL_DEBUG_TYPE_ERROR: - case GL_DEBUG_TYPE_ERROR_ARB: - outStr += "[OPENGL API ERROR]\t\t"; - break; - //case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: - outStr += "[OPENGL DEPRECATED]\t\t"; - break; - //case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: - outStr += "[OPENGL UNDEFINED]\t\t"; - break; - //case GL_DEBUG_TYPE_PORTABILITY: - case GL_DEBUG_TYPE_PORTABILITY_ARB: - outStr += "[OPENGL PORTABILITY]\t\t"; - break; - //case GL_DEBUG_TYPE_PERFORMANCE: - case GL_DEBUG_TYPE_PERFORMANCE_ARB: - outStr += "[OPENGL PERFORMANCE]\t\t"; - break; - default: - outStr += "[OPENGL OTHER]\t\t"; - ///return; - break; - } - outStr += message; - break; - //case GL_DEBUG_SOURCE_SHADER_COMPILER: - case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: - outStr += "[SHADER]\t\t"; - outStr += message; - break; - //case GL_DEBUG_SOURCE_WINDOW_SYSTEM: - case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: - outStr += "[WINDOW SYS]\t\t"; - outStr += message; - break; - //case GL_DEBUG_SOURCE_THIRD_PARTY: - case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: - outStr += "[3RDPARTY]\t\t"; - outStr += message; - break; - //case GL_DEBUG_SOURCE_APPLICATION: - case GL_DEBUG_SOURCE_APPLICATION_ARB: - outStr += "[APP]\t\t"; - outStr += message; - break; - //case GL_DEBUG_SOURCE_OTHER: - case GL_DEBUG_SOURCE_OTHER_ARB: - outStr += "[OTHER]\t\t"; - outStr += message; - break; - default: - break; - } - outStr += "\n"; - printf("%s",outStr.c_str()); -} -#endif // OPENGL_DEBUG - - - -//! NEW, Uniform Buffer Objects! -struct PerFrameUniformBlock -{ - vectorSIMDf dynamicResolutionScale; //with 2 float padding - struct { - float dynResScale[2]; - uint32_t percentileSearchVals[2]; - } autoExposureInput; - struct { - float autoExposureParameters[2]; - uint32_t padding[2]; - } autoExposureOutput; -}; - - +core::smart_refctd_ptr createImageForLackOfEXRLoader(asset::IAssetManager* am, video::IVideoDriver* driver); int main() { - irr::SIrrlichtCreationParameters params; - params.Bits = 24; //may have to set to 32bit for some platforms - params.ZBufferBits = 24; //we'd like 32bit here - params.DriverType = video::EDT_OPENGL; //! Only Well functioning driver, software renderer left for sake of 2D image drawing - params.WindowSize = dimension2d(1280, 720); - params.Fullscreen = false; - params.Vsync = true; //! If supported by target platform - params.Doublebuffer = true; - params.Stencilbuffer = false; //! This will not even be a choice soon - - //load 16bit float screencap dump and its dimensions - core::dimension2du dynamicResolutionSize; - FILE *fp = fopen("../../media/preExposureScreenDump.dims", "r+"); - if (fp) - { - int ret = fscanf(fp, "%d %d \t %d %d", - ¶ms.WindowSize.Width, ¶ms.WindowSize.Height, - &dynamicResolutionSize.Width, &dynamicResolutionSize.Height); - // - if(ret != 4) - { - printf("Couldn't get screendump sizes!\n"); - return 2; - } - fclose(fp); - } - else - return 3; - - void* tmpLoadingMem = malloc(dynamicResolutionSize.Width*dynamicResolutionSize.Height*16); - fp = fopen("../../media/preExposureScreenDump.rgba16f", "r+"); - if (!fp) - { - free(tmpLoadingMem); - return 4; - } - fread(tmpLoadingMem,dynamicResolutionSize.Width*dynamicResolutionSize.Height*8,1,fp); - - - IrrlichtDevice* device = createDeviceEx(params); + irr::SIrrlichtCreationParameters deviceParams; + deviceParams.Bits = 24; //may have to set to 32bit for some platforms + deviceParams.ZBufferBits = 24; //we'd like 32bit here + deviceParams.DriverType = video::EDT_OPENGL; //! Only Well functioning driver, software renderer left for sake of 2D image drawing + deviceParams.WindowSize = dimension2d(1280, 720); + deviceParams.Fullscreen = false; + deviceParams.Vsync = true; //! If supported by target platform + deviceParams.Doublebuffer = true; + deviceParams.Stencilbuffer = false; //! This will not even be a choice soon + + IrrlichtDevice* device = createDeviceEx(deviceParams); if (device == 0) return 1; // could not create selected driver. @@ -177,108 +36,233 @@ int main() device->setEventReceiver(&receiver); video::IVideoDriver* driver = device->getVideoDriver(); -#ifdef OPENGL_DEBUG - if (video::COpenGLExtensionHandler::FeatureAvailable[video::COpenGLExtensionHandler::IRR_KHR_debug]) - { - glEnable(GL_DEBUG_OUTPUT); - //glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - video::COpenGLExtensionHandler::pGlDebugMessageControl(GL_DONT_CARE,GL_DONT_CARE,GL_DONT_CARE,0,NULL,true); - - video::COpenGLExtensionHandler::pGlDebugMessageCallback(openGLCBFunc,NULL); - } - else - { - //glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - video::COpenGLExtensionHandler::pGlDebugMessageControlARB(GL_DONT_CARE,GL_DONT_CARE,GL_DONT_CARE,0,NULL,true); - - video::COpenGLExtensionHandler::pGlDebugMessageCallbackARB(openGLCBFunc,NULL); - } -#endif // OPENGL_DEBUG - - //upload screendump to a texture - auto hdrTex = driver->createGPUTexture(video::ITexture::ETT_2D,¶ms.WindowSize.Width,1,asset::EF_R16G16B16A16_SFLOAT); - uint32_t zeroArray[3] = {0,0,0}; - uint32_t subSize[3] = {dynamicResolutionSize.Width,dynamicResolutionSize.Height,1}; - hdrTex->updateSubRegion(asset::EF_R16G16B16A16_SFLOAT,tmpLoadingMem,zeroArray,subSize); - free(tmpLoadingMem); - - - auto fullScreenTriangle = ext::FullScreenTriangle::createFullScreenTriangle(driver); - - video::SGPUMaterial postProcMaterial; - //! First need to make a material other than default to be able to draw with custom shader - postProcMaterial.BackfaceCulling = false; //! Triangles will be visible from both sides - postProcMaterial.ZBuffer = video::ECFN_ALWAYS; //! Ignore Depth Test - postProcMaterial.ZWriteEnable = false; //! Why even write depth? - postProcMaterial.MaterialType = (video::E_MATERIAL_TYPE)driver->getGPUProgrammingServices()->addHighLevelShaderMaterialFromFiles("../fullscreentri.vert", - "","","", //! No Geometry or Tessellation Shaders - "../postproc.frag", - 3,video::EMT_SOLID); //! 3 vertices per primitive (this is tessellation shader relevant only) - postProcMaterial.setTexture(0,core::smart_refctd_ptr(hdrTex)); - - + + io::IFileSystem* filesystem = device->getFileSystem(); + asset::IAssetManager* am = device->getAssetManager(); + + + core::smart_refctd_ptr inImg; + { + inImg = createImageForLackOfEXRLoader(am,driver); + + } + + core::smart_refctd_ptr imgToTonemap; + core::smart_refctd_ptr outImg,outImgStorage; + { + video::IGPUImageView::SCreationParams imgViewInfo; + imgViewInfo.flags = static_cast(0u); + imgViewInfo.image = core::smart_refctd_ptr(inImg); // copy smart ptr, still want to use it + imgViewInfo.viewType = video::IGPUImageView::ET_2D; + imgViewInfo.format = asset::EF_R32G32_UINT; + imgViewInfo.subresourceRange.baseMipLevel = 0; + imgViewInfo.subresourceRange.levelCount = 1; + imgViewInfo.subresourceRange.baseArrayLayer = 0; + imgViewInfo.subresourceRange.layerCount = 1; + imgToTonemap = driver->createGPUImageView(std::move(imgViewInfo)); + + video::IGPUImage::SCreationParams imgInfo = inImg->getCreationParameters(); + imgInfo.format = asset::EF_R8G8B8A8_SRGB; + imgViewInfo.image = driver->createDeviceLocalGPUImageOnDedMem(std::move(imgInfo)); + imgViewInfo.format = asset::EF_R8G8B8A8_SRGB; + outImg = driver->createGPUImageView(std::move(imgViewInfo)); + imgViewInfo.format = asset::EF_R32_UINT; + outImgStorage = driver->createGPUImageView(std::move(imgViewInfo)); + } + - irr::ext::AutoExposure::CToneMapper* toneMapper = irr::ext::AutoExposure::CToneMapper::instantiateTonemapper(driver, - "../../../ext/AutoExposure/lumaHistogramFirstPass.comp", - "../../../ext/AutoExposure/lumaHistogramSecondPass.comp", - offsetof(PerFrameUniformBlock,autoExposureInput.dynResScale), - offsetof(PerFrameUniformBlock,autoExposureInput.percentileSearchVals), - offsetof(PerFrameUniformBlock,autoExposureOutput)); + auto tonemapper = ext::AutoExposure::CToneMapper::create(driver,inImg->getCreationParameters().format,am->getGLSLCompiler()); - PerFrameUniformBlock block; - block.dynamicResolutionScale = vectorSIMDf(dynamicResolutionSize.Width,dynamicResolutionSize.Height,dynamicResolutionSize.Width,dynamicResolutionSize.Height); - block.dynamicResolutionScale /= vectorSIMDf(params.WindowSize.Width,params.WindowSize.Height,params.WindowSize.Width,params.WindowSize.Height); + // TODO: employ luma histograming + auto params = ext::AutoExposure::ReinhardParams::fromKeyAndBurn(0.18, 0.95, 8.0, 16.0); + auto parameterBuffer = driver->createFilledDeviceLocalGPUBufferOnDedMem(sizeof(ext::AutoExposure::ReinhardParams),¶ms); - toneMapper->setHistogramSamplingRate(block.autoExposureInput.dynResScale,block.autoExposureInput.percentileSearchVals, //out - dynamicResolutionSize,block.dynamicResolutionScale.pointer); //in + auto descriptorSet = driver->createGPUDescriptorSet(core::smart_refctd_ptr(tonemapper->getDescriptorSetLayout())); + { + video::IGPUDescriptorSet::SDescriptorInfo pInfos[3]; + pInfos[0].desc = parameterBuffer; + pInfos[0].buffer.offset = 0u; + pInfos[0].buffer.size = video::IGPUBufferView::whole_buffer; + pInfos[1].desc = imgToTonemap; + pInfos[1].image.imageLayout = static_cast(0u); + using S = asset::ISampler; + pInfos[1].image.sampler = driver->createGPUSampler({ {S::ETC_CLAMP_TO_EDGE,S::ETC_CLAMP_TO_EDGE,S::ETC_CLAMP_TO_EDGE, + S::ETBC_FLOAT_OPAQUE_BLACK, + S::ETF_NEAREST,S::ETF_NEAREST,S::ESMM_NEAREST,0u, + false,asset::ECO_ALWAYS}, + 0.f,-FLT_MAX,FLT_MAX}); + pInfos[2].desc = outImgStorage; + pInfos[2].image.imageLayout = static_cast(0u); + pInfos[2].image.sampler = nullptr; + + video::IGPUDescriptorSet::SWriteDescriptorSet pWrites[3]; + pWrites[0].dstSet = descriptorSet.get(); + pWrites[0].binding = 0u; + pWrites[0].arrayElement = 0u; + pWrites[0].count = 1u; + pWrites[0].descriptorType = asset::EDT_UNIFORM_BUFFER_DYNAMIC; + pWrites[0].info = pInfos+0u; + pWrites[1].dstSet = descriptorSet.get(); + pWrites[1].binding = 1u; + pWrites[1].arrayElement = 0u; + pWrites[1].count = 1u; + pWrites[1].descriptorType = asset::EDT_COMBINED_IMAGE_SAMPLER; + pWrites[1].info = pInfos+1u; + pWrites[2].dstSet = descriptorSet.get(); + pWrites[2].binding = 2u; + pWrites[2].arrayElement = 0u; + pWrites[2].count = 1u; + pWrites[2].descriptorType = asset::EDT_STORAGE_IMAGE; + pWrites[2].info = pInfos+2u; + driver->updateDescriptorSets(3u,pWrites,0u,nullptr); + } - video::IGPUBuffer* frameUniformBuffer = driver->createFilledDeviceLocalGPUBufferOnDedMem(sizeof(block),&block); + auto blitFBO = driver->addFrameBuffer(); + blitFBO->attach(video::EFAP_COLOR_ATTACHMENT0, std::move(outImg)); - uint64_t lastFPSTime = 0; - - while(device->run()&&receiver.keepOpen()) + while (device->run() && receiver.keepOpen()) { - driver->beginScene( false,false ); - + driver->beginScene(false, false); - toneMapper->CalculateFrameExposureFactors(frameUniformBuffer,frameUniformBuffer,core::smart_refctd_ptr(hdrTex)); - - - const video::COpenGLDriver::SAuxContext* foundConst = static_cast(driver)->getThreadContext(); - video::COpenGLDriver::SAuxContext* found = const_cast(foundConst); - //set UBO - { - const video::COpenGLBuffer* buffers[1] = {static_cast(frameUniformBuffer)}; - ptrdiff_t offsets[1] = {0}; - ptrdiff_t sizes[1] = {frameUniformBuffer->getSize()}; - found->setActiveUBO(0,1,buffers,offsets,sizes); - } - driver->setMaterial(postProcMaterial); - driver->drawMeshBuffer(fullScreenTriangle.get()); + tonemapper->tonemap(imgToTonemap.get(), descriptorSet.get(), 0u); + + video::COpenGLExtensionHandler::extGlMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT | GL_TEXTURE_FETCH_BARRIER_BIT); + driver->blitRenderTargets(blitFBO, nullptr, false, false); driver->endScene(); - - // display frames per second in window title - uint64_t time = device->getTimer()->getRealTime(); - if (time-lastFPSTime > 1000) - { - std::wostringstream str; - str << L"Autoexposure Demo - Irrlicht Engine [" << driver->getName() << "] FPS:" << driver->getFPS() << " PrimitvesDrawn:" << driver->getPrimitiveCountDrawn(); - - device->setWindowCaption(str.str()); - lastFPSTime = time; - } } + // TODO: Histogramming +#if 0 + toneMapper->CalculateFrameExposureFactors(frameUniformBuffer,frameUniformBuffer,core::smart_refctd_ptr(hdrTex)); - toneMapper->drop(); //create a screenshot { core::rect sourceRect(0, 0, params.WindowSize.Width, params.WindowSize.Height); ext::ScreenShot::dirtyCPUStallingScreenshot(device, "screenshot.png", sourceRect, asset::EF_R8G8B8_SRGB); } - +#endif device->drop(); return 0; } + + +core::smart_refctd_ptr createImageForLackOfEXRLoader(asset::IAssetManager* am, video::IVideoDriver* driver) +{ + auto viewportSize = driver->getScreenSize(); + + video::IGPUImage::SCreationParams imgInfo; + imgInfo.flags = static_cast(0u); + imgInfo.type = video::IGPUImage::ET_2D; + imgInfo.format = asset::EF_R16G16B16A16_SFLOAT; + imgInfo.extent = {viewportSize.Width,viewportSize.Height,1}; + imgInfo.mipLevels = 1u; + imgInfo.arrayLayers = 1u; + imgInfo.samples = video::IGPUImage::ESCF_1_BIT; + + auto img = driver->createDeviceLocalGPUImageOnDedMem(std::move(imgInfo)); + { + video::IGPUDescriptorSetLayout::SBinding bnd; + bnd.binding = 0u; + bnd.type = asset::EDT_STORAGE_IMAGE; + bnd.count = 1u; + bnd.stageFlags = asset::ESS_COMPUTE; + auto dsLayout = driver->createGPUDescriptorSetLayout(&bnd, &bnd+1); + + auto layout = driver->createGPUPipelineLayout(nullptr, nullptr, nullptr, nullptr, nullptr, core::smart_refctd_ptr(dsLayout)); + + core::smart_refctd_ptr shader; + { + const char* glsl = R"===( +#version 430 core +layout (local_size_x = 16, local_size_y = 16) in; + +layout(set=3, binding=0, rgba16f) uniform writeonly restrict image2D outImg; + +// The MIT License +// Copyright © 2015 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A simple way to create color variation in a cheap way (yes, trigonometrics ARE cheap +// in the GPU, don't try to be smart and use a triangle wave instead). + +// See http://iquilezles.org/www/articles/palettes/palettes.htm for more information + + +vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d ) +{ + return a + b*cos( 6.28318*(c*t+d) ); +} + +void main() +{ + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + ivec2 resolution = imageSize(outImg); + if (any(greaterThanEqual(uv,resolution))) + return; + + vec2 p = vec2(uv)/vec2(resolution); + + // compute colors + vec3 col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.33,0.67) ); + if( p.y>(1.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.10,0.20) ); + if( p.y>(2.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.3,0.20,0.20) ); + if( p.y>(3.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,0.5),vec3(0.8,0.90,0.30) ); + if( p.y>(4.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,0.7,0.4),vec3(0.0,0.15,0.20) ); + if( p.y>(5.0/7.0) ) col = pal( p.x, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(2.0,1.0,0.0),vec3(0.5,0.20,0.25) ); + if( p.y>(6.0/7.0) ) col = pal( p.x, vec3(0.8,0.5,0.4),vec3(0.2,0.4,0.2),vec3(2.0,1.0,1.0),vec3(0.0,0.25,0.25) ); + + + // band + float f = fract(p.y*7.0); + // borders + col *= smoothstep( 0.49, 0.47, abs(f-0.5) ); + // shadowing + col *= 0.5 + 0.5*sqrt(4.0*f*(1.0-f)); + + col *= 16.0; + + imageStore(outImg,uv,vec4(col,1.0)); +} +)==="; + + auto spirv = am->getGLSLCompiler()->createSPIRVFromGLSL(glsl, asset::ESS_COMPUTE, "main", "gradient"); + auto cs_unspec = driver->createGPUShader(std::move(spirv)); + + auto specInfo = core::make_smart_refctd_ptr(core::vector{}, nullptr, "main", asset::ESS_COMPUTE); + + shader = driver->createGPUSpecializedShader(cs_unspec.get(),specInfo.get()); + } + + auto pipeline = driver->createGPUComputePipeline(nullptr, std::move(layout), std::move(shader)); + + auto descriptorSet = driver->createGPUDescriptorSet(std::move(dsLayout)); + { + video::IGPUDescriptorSet::SDescriptorInfo info; + info.desc = driver->createGPUImageView({static_cast(0u), + core::smart_refctd_ptr(img), + video::IGPUImageView::ET_2D, + asset::EF_R16G16B16A16_SFLOAT, + {}, + {static_cast(0u),0u,1u,0u,1u}}); + info.image.imageLayout = static_cast(0u); + info.image.sampler = nullptr; + + video::IGPUDescriptorSet::SWriteDescriptorSet write; + write.dstSet = descriptorSet.get(); + write.binding = 0u; + write.arrayElement = 0u; + write.count = 1u; + write.descriptorType = asset::EDT_STORAGE_IMAGE; + write.info = &info; + driver->updateDescriptorSets(1u, &write, 0u, nullptr); + } + + driver->bindComputePipeline(pipeline.get()); + driver->bindDescriptorSets(video::EPBP_COMPUTE,pipeline->getLayout(),3u,1u,&descriptorSet.get(),nullptr); + driver->dispatch(imgInfo.extent.width/16u,imgInfo.extent.height/16u,1u); + } + + return img; +} \ No newline at end of file