Przeglądaj źródła

Added a hardware accelerated way to scale cubemaps

BearishSun 8 lat temu
rodzic
commit
d925079bef

+ 22 - 0
Source/BansheeEngine/Include/BsReflectionProbes.h

@@ -77,7 +77,29 @@ namespace bs { namespace ct
 		 */
 		 */
 		static void filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch);
 		static void filterCubemapForSpecular(const SPtr<Texture>& cubemap, const SPtr<Texture>& scratch);
 
 
+		/**
+		 * Scales a cubemap and outputs it in the destination texture, using hardware acceleration. If both textures are the
+		 * same size, performs a copy instead.
+		 *
+		 * @param[in]   src				Source cubemap to scale.
+		 * @param[in]   srcMip			Determines which mip level of the source texture to scale.
+		 * @param[in]   dst				Desination texture to output the scaled data to. Must be usable as a render target.
+		 * @param[in]   dstMip			Determines which mip level of the destination texture to scale.
+		 */
+		static void scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip);
+
 		static const UINT32 REFLECTION_CUBEMAP_SIZE;
 		static const UINT32 REFLECTION_CUBEMAP_SIZE;
+
+	private:
+		/** 
+		 * Downsamples a cubemap using hardware bilinear filtering. 
+		 * 
+		 * @param[in]	src		Cubemap to downsample.
+		 * @param[in]   srcMip	Determines which mip level of the source texture to downsample.
+		 * @param[in]   dst		Desination texture to output the scaled data to. Must be usable as a render target.
+		 * @param[in]   dstMip			Determines which mip level of the destination texture to scale.
+		 */
+		static void downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip);
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 69 - 14
Source/BansheeEngine/Source/BsReflectionProbes.cpp

@@ -93,6 +93,7 @@ namespace bs { namespace ct
 			cubemapDesc.width = props.getWidth();
 			cubemapDesc.width = props.getWidth();
 			cubemapDesc.height = props.getHeight();
 			cubemapDesc.height = props.getHeight();
 			cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
 			cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
+			cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
 
 
 			scratchCubemap = Texture::create(cubemapDesc);
 			scratchCubemap = Texture::create(cubemapDesc);
 		}
 		}
@@ -115,20 +116,8 @@ namespace bs { namespace ct
 		// Fill out remaining scratch mip levels by downsampling
 		// Fill out remaining scratch mip levels by downsampling
 		for (UINT32 mip = 1; mip < numMips; mip++)
 		for (UINT32 mip = 1; mip < numMips; mip++)
 		{
 		{
-			for (UINT32 face = 0; face < 6; face++)
-			{
-				RENDER_TEXTURE_DESC cubeFaceRTDesc;
-				cubeFaceRTDesc.colorSurfaces[0].texture = scratchCubemap;
-				cubeFaceRTDesc.colorSurfaces[0].face = face;
-				cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
-				cubeFaceRTDesc.colorSurfaces[0].mipLevel = mip;
-
-				SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
-
-				UINT32 sourceMip = mip - 1;
-				TextureSurface sourceSurface(sourceMip, 1, 0, 6);
-				downsampleMat.execute(scratchCubemap, face, sourceSurface, target);
-			}
+			UINT32 sourceMip = mip - 1;
+			downsampleCubemap(scratchCubemap, sourceMip, scratchCubemap, mip);
 		}
 		}
 
 
 		// Importance sample
 		// Importance sample
@@ -148,4 +137,70 @@ namespace bs { namespace ct
 			}
 			}
 		}
 		}
 	}
 	}
+
+	void ReflectionProbes::scaleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip)
+	{
+		static ReflectionCubeDownsampleMat downsampleMat;
+
+		auto& srcProps = src->getProperties();
+		auto& dstProps = dst->getProperties();
+
+		SPtr<Texture> scratchTex = src;
+		int sizeSrcLog2 = (int)log2((float)srcProps.getWidth());
+		int sizeDstLog2 = (int)log2((float)dstProps.getWidth());
+
+		int sizeLog2Diff = sizeSrcLog2 - sizeDstLog2;
+
+		// If size difference is greater than one mip-level and we're downscaling, we need to generate intermediate mip
+		// levels
+		if(sizeLog2Diff > 1)
+		{
+			UINT32 mipSize = (UINT32)exp2((float)(sizeSrcLog2 - 1));
+			UINT32 numDownsamples = sizeLog2Diff - 1;
+
+			TEXTURE_DESC cubemapDesc;
+			cubemapDesc.type = TEX_TYPE_CUBE_MAP;
+			cubemapDesc.format = srcProps.getFormat();
+			cubemapDesc.width = mipSize;
+			cubemapDesc.height = mipSize;
+			cubemapDesc.numMips = numDownsamples - 1;
+			cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
+
+			scratchTex = Texture::create(cubemapDesc);
+
+			downsampleCubemap(src, srcMip, scratchTex, 0);
+			for(UINT32 i = 0; i < cubemapDesc.numMips; i++)
+				downsampleCubemap(scratchTex, i, scratchTex, i + 1);
+
+			srcMip = cubemapDesc.numMips;
+		}
+
+		// Same size so just copy
+		if(sizeSrcLog2 == sizeDstLog2)
+		{
+			for (UINT32 face = 0; face < 6; face++)
+				src->copy(dst, face, srcMip, face, dstMip);
+		}
+		else
+			downsampleCubemap(src, srcMip, dst, dstMip);
+	}
+
+	void ReflectionProbes::downsampleCubemap(const SPtr<Texture>& src, UINT32 srcMip, const SPtr<Texture>& dst, UINT32 dstMip)
+	{
+		static ReflectionCubeDownsampleMat downsampleMat;
+
+		for (UINT32 face = 0; face < 6; face++)
+		{
+			RENDER_TEXTURE_DESC cubeFaceRTDesc;
+			cubeFaceRTDesc.colorSurfaces[0].texture = dst;
+			cubeFaceRTDesc.colorSurfaces[0].face = face;
+			cubeFaceRTDesc.colorSurfaces[0].numFaces = 1;
+			cubeFaceRTDesc.colorSurfaces[0].mipLevel = dstMip;
+
+			SPtr<RenderTarget> target = RenderTexture::create(cubeFaceRTDesc);
+
+			TextureSurface sourceSurface(srcMip, 1, 0, 6);
+			downsampleMat.execute(src, face, sourceSurface, target);
+		}
+	}
 }}
 }}

+ 68 - 95
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -42,8 +42,8 @@ using namespace std::placeholders;
 
 
 namespace bs { namespace ct
 namespace bs { namespace ct
 {
 {
-    // Limited by max number of array elements in texture for DX11 hardware
-    constexpr UINT32 MaxReflectionCubemaps = 2048 / 6;
+	// Limited by max number of array elements in texture for DX11 hardware
+	constexpr UINT32 MaxReflectionCubemaps = 2048 / 6;
 
 
 	RenderBeast::RenderBeast()
 	RenderBeast::RenderBeast()
 		: mDefaultMaterial(nullptr)
 		: mDefaultMaterial(nullptr)
@@ -94,7 +94,7 @@ namespace bs { namespace ct
 
 
 		mTiledDeferredLightingMats = bs_new<TiledDeferredLightingMaterials>();
 		mTiledDeferredLightingMats = bs_new<TiledDeferredLightingMaterials>();
 
 
-        mPreintegratedEnvBRDF = TiledDeferredLighting::generatePreintegratedEnvBRDF();
+		mPreintegratedEnvBRDF = TiledDeferredLighting::generatePreintegratedEnvBRDF();
 		mGPULightData = bs_new<GPULightData>();
 		mGPULightData = bs_new<GPULightData>();
 		mGPUReflProbeData = bs_new<GPUReflProbeData>();
 		mGPUReflProbeData = bs_new<GPUReflProbeData>();
 		mLightGrid = bs_new<LightGrid>();
 		mLightGrid = bs_new<LightGrid>();
@@ -120,8 +120,8 @@ namespace bs { namespace ct
 		mRenderableVisibility.clear();
 		mRenderableVisibility.clear();
 
 
 		mReflCubemapArrayTex = nullptr;
 		mReflCubemapArrayTex = nullptr;
-        mSkyboxTexture = nullptr;
-        mSkyboxFilteredReflections = nullptr;
+		mSkyboxTexture = nullptr;
+		mSkyboxFilteredReflections = nullptr;
 
 
 		PostProcessing::shutDown();
 		PostProcessing::shutDown();
 		GpuResourcePool::shutDown();
 		GpuResourcePool::shutDown();
@@ -135,7 +135,7 @@ namespace bs { namespace ct
 		bs_delete(mFlatFramebufferToTextureMat);
 		bs_delete(mFlatFramebufferToTextureMat);
 		bs_delete(mTiledDeferredLightingMats);
 		bs_delete(mTiledDeferredLightingMats);
 
 
-        mPreintegratedEnvBRDF = nullptr;
+		mPreintegratedEnvBRDF = nullptr;
 
 
 		RendererUtility::shutDown();
 		RendererUtility::shutDown();
 
 
@@ -494,11 +494,11 @@ namespace bs { namespace ct
 				mCubemapArrayUsedSlots.push_back(true);
 				mCubemapArrayUsedSlots.push_back(true);
 			}
 			}
 
 
-            if(probeInfo.arrayIdx > MaxReflectionCubemaps)
-            {
-                LOGERR("Reached the maximum number of allowed reflection probe cubemaps at once. "
-                       "Ignoring reflection probe data.");
-            }
+			if(probeInfo.arrayIdx > MaxReflectionCubemaps)
+			{
+				LOGERR("Reached the maximum number of allowed reflection probe cubemaps at once. "
+					"Ignoring reflection probe data.");
+			}
 		}
 		}
 	}
 	}
 
 
@@ -542,31 +542,34 @@ namespace bs { namespace ct
 		ReflectionCubemapCache::instance().unloadCachedTexture(probe->getUUID());
 		ReflectionCubemapCache::instance().unloadCachedTexture(probe->getUUID());
 	}
 	}
 
 
-    void RenderBeast::notifySkyboxAdded(Skybox* skybox)
+	void RenderBeast::notifySkyboxAdded(Skybox* skybox)
 	{
 	{
-        mSkybox = skybox;
+		mSkybox = skybox;
 
 
-        SPtr<Texture> skyTex = skybox->getTexture();
-        if (skyTex != nullptr && skyTex->getProperties().getTextureType() == TEX_TYPE_CUBE_MAP)
-            mSkyboxTexture = skyTex;
+		SPtr<Texture> skyTex = skybox->getTexture();
+		if (skyTex != nullptr && skyTex->getProperties().getTextureType() == TEX_TYPE_CUBE_MAP)
+			mSkyboxTexture = skyTex;
 
 
-        mSkyboxFilteredReflections = nullptr;
+		mSkyboxFilteredReflections = nullptr;
 	}
 	}
 
 
-    void RenderBeast::notifySkyboxTextureChanged(Skybox* skybox)
+	void RenderBeast::notifySkyboxTextureChanged(Skybox* skybox)
 	{
 	{
-        ReflectionCubemapCache::instance().notifyDirty(skybox->getUUID());
+		ReflectionCubemapCache::instance().notifyDirty(skybox->getUUID());
 
 
-        if (mSkybox == skybox)
-            mSkyboxFilteredReflections = nullptr;
+		if (mSkybox == skybox)
+		{
+			mSkyboxTexture = skybox->getTexture();
+			mSkyboxFilteredReflections = nullptr;
+		}
 	}
 	}
 
 
-    void RenderBeast::notifySkyboxRemoved(Skybox* skybox)
+	void RenderBeast::notifySkyboxRemoved(Skybox* skybox)
 	{
 	{
-        ReflectionCubemapCache::instance().unloadCachedTexture(skybox->getUUID());
+		ReflectionCubemapCache::instance().unloadCachedTexture(skybox->getUUID());
 
 
-        if (mSkybox == skybox)
-            mSkyboxTexture = nullptr;
+		if (mSkybox == skybox)
+			mSkyboxTexture = nullptr;
 	}
 	}
 
 
 	SPtr<PostProcessSettings> RenderBeast::createPostProcessSettings() const
 	SPtr<PostProcessSettings> RenderBeast::createPostProcessSettings() const
@@ -637,7 +640,7 @@ namespace bs { namespace ct
 			viewDesc.noLighting = camera->getFlags().isSet(CameraFlag::NoLighting);
 			viewDesc.noLighting = camera->getFlags().isSet(CameraFlag::NoLighting);
 			viewDesc.triggerCallbacks = true;
 			viewDesc.triggerCallbacks = true;
 			viewDesc.runPostProcessing = true;
 			viewDesc.runPostProcessing = true;
-            viewDesc.renderingReflections = false;
+			viewDesc.renderingReflections = false;
 
 
 			viewDesc.cullFrustum = camera->getWorldFrustum();
 			viewDesc.cullFrustum = camera->getWorldFrustum();
 			viewDesc.visibleLayers = camera->getLayers();
 			viewDesc.visibleLayers = camera->getLayers();
@@ -896,14 +899,14 @@ namespace bs { namespace ct
 			mReflProbes[i].getParameters(mReflProbeDataTemp.back());
 			mReflProbes[i].getParameters(mReflProbeDataTemp.back());
 		}
 		}
 
 
-        // Sort probes so bigger ones get accessed first, this way we overlay smaller ones on top of biggers ones when
-	    // rendering
-        auto sorter = [](const ReflProbeData& lhs, const ReflProbeData& rhs)
-        {
-            return rhs.radius < lhs.radius;
-        };
+		// Sort probes so bigger ones get accessed first, this way we overlay smaller ones on top of biggers ones when
+		// rendering
+		auto sorter = [](const ReflProbeData& lhs, const ReflProbeData& rhs)
+		{
+			return rhs.radius < lhs.radius;
+		};
 
 
-        std::sort(mReflProbeDataTemp.begin(), mReflProbeDataTemp.end(), sorter);
+		std::sort(mReflProbeDataTemp.begin(), mReflProbeDataTemp.end(), sorter);
 
 
 		mGPUReflProbeData->setProbes(mReflProbeDataTemp, numProbes);
 		mGPUReflProbeData->setProbes(mReflProbeDataTemp, numProbes);
 
 
@@ -1031,12 +1034,12 @@ namespace bs { namespace ct
 		rapi.setRenderTarget(nullptr);
 		rapi.setRenderTarget(nullptr);
 
 
 		// Render light pass
 		// Render light pass
-        UINT32 numSamples = viewInfo->getNumSamples();
+		UINT32 numSamples = viewInfo->getNumSamples();
 		ITiledDeferredLightingMat* lightingMat = mTiledDeferredLightingMats->get(numSamples, viewInfo->isRenderingReflections());
 		ITiledDeferredLightingMat* lightingMat = mTiledDeferredLightingMats->get(numSamples, viewInfo->isRenderingReflections());
 
 
 		lightingMat->setLights(*mGPULightData);
 		lightingMat->setLights(*mGPULightData);
-        lightingMat->setReflectionProbes(*mGPUReflProbeData, mReflCubemapArrayTex);
-        lightingMat->setSkyReflections(mSkyboxFilteredReflections);
+		lightingMat->setReflectionProbes(*mGPUReflProbeData, mReflCubemapArrayTex);
+		lightingMat->setSkyReflections(mSkyboxFilteredReflections);
 
 
 		lightingMat->execute(renderTargets, perCameraBuffer, mPreintegratedEnvBRDF, viewInfo->renderWithNoLighting());
 		lightingMat->execute(renderTargets, perCameraBuffer, mPreintegratedEnvBRDF, viewInfo->renderWithNoLighting());
 
 
@@ -1224,10 +1227,10 @@ namespace bs { namespace ct
 
 
 		bs_frame_mark();
 		bs_frame_mark();
 		{		
 		{		
-            UINT32 currentCubeArraySize = 0;
-		    
-            if(mReflCubemapArrayTex != nullptr)
-		        mReflCubemapArrayTex->getProperties().getNumArraySlices();
+			UINT32 currentCubeArraySize = 0;
+
+			if(mReflCubemapArrayTex != nullptr)
+				mReflCubemapArrayTex->getProperties().getNumArraySlices();
 
 
 			bool forceArrayUpdate = false;
 			bool forceArrayUpdate = false;
 			if(mReflCubemapArrayTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))
 			if(mReflCubemapArrayTex == nullptr || (currentCubeArraySize < numProbes && currentCubeArraySize != MaxReflectionCubemaps))
@@ -1253,6 +1256,7 @@ namespace bs { namespace ct
 			cubemapDesc.width = ReflectionProbes::REFLECTION_CUBEMAP_SIZE;
 			cubemapDesc.width = ReflectionProbes::REFLECTION_CUBEMAP_SIZE;
 			cubemapDesc.height = ReflectionProbes::REFLECTION_CUBEMAP_SIZE;
 			cubemapDesc.height = ReflectionProbes::REFLECTION_CUBEMAP_SIZE;
 			cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
 			cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
+			cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
 
 
 			SPtr<Texture> scratchCubemap;
 			SPtr<Texture> scratchCubemap;
 			if (numProbes > 0)
 			if (numProbes > 0)
@@ -1263,8 +1267,8 @@ namespace bs { namespace ct
 			{
 			{
 				RendererReflectionProbe& probeInfo = mReflProbes[i];
 				RendererReflectionProbe& probeInfo = mReflProbes[i];
 
 
-                if (probeInfo.arrayIdx > MaxReflectionCubemaps)
-                    continue;
+				if (probeInfo.arrayIdx > MaxReflectionCubemaps)
+					continue;
 
 
 				if (probeInfo.probe->getType() != ReflectionProbeType::Plane)
 				if (probeInfo.probe->getType() != ReflectionProbeType::Plane)
 				{
 				{
@@ -1282,23 +1286,7 @@ namespace bs { namespace ct
 						else
 						else
 						{
 						{
 							SPtr<Texture> customTexture = probeInfo.probe->getCustomTexture();
 							SPtr<Texture> customTexture = probeInfo.probe->getCustomTexture();
-
-							// Note: If the Texture class supported scale on copy we could avoid using CPU reads & buffers
-							auto& texProps = probeInfo.texture->getProperties();
-							SPtr<PixelData> scaledFaceData = texProps.allocBuffer(0, 0);
-
-							auto& customTexProps = customTexture->getProperties();
-							SPtr<PixelData> originalFaceData = customTexProps.allocBuffer(0, 0);
-							for (UINT32 j = 0; j < 6; j++)
-							{
-								if (j < customTexProps.getNumFaces())
-									customTexture->readData(*originalFaceData, 0, j);
-
-								PixelUtil::scale(*originalFaceData, *scaledFaceData);
-
-								probeInfo.texture->writeData(*scaledFaceData, 0, j);
-
-							}
+							ReflectionProbes::scaleCubemap(customTexture, 0, probeInfo.texture, 0);
 						}
 						}
 
 
 						ReflectionProbes::filterCubemapForSpecular(probeInfo.texture, scratchCubemap);
 						ReflectionProbes::filterCubemapForSpecular(probeInfo.texture, scratchCubemap);
@@ -1341,41 +1329,26 @@ namespace bs { namespace ct
 				// Note: Consider pruning the reflection cubemap array if empty slot count becomes too high
 				// Note: Consider pruning the reflection cubemap array if empty slot count becomes too high
 			}
 			}
 
 
-            // Get reflections for skybox if needed/available
-            if (mSkybox != nullptr && mSkyboxTexture != nullptr)
-            {
-                // If haven't assigned them already, do it now
-                if(mSkyboxFilteredReflections == nullptr)
-                {
-                    if (!ReflectionCubemapCache::instance().isDirty(mSkybox->getUUID()))
-                        mSkyboxFilteredReflections = ReflectionCubemapCache::instance().getCachedTexture(mSkybox->getUUID());
-                    else
-                    {
-                        mSkyboxFilteredReflections = Texture::create(cubemapDesc);
-
-                        // Note: If the Texture class supported scale on copy we could avoid using CPU reads & buffers
-                        auto& texProps = mSkyboxFilteredReflections->getProperties();
-                        SPtr<PixelData> scaledFaceData = texProps.allocBuffer(0, 0);
-
-                        auto& skyboxTex = mSkyboxTexture->getProperties();
-                        SPtr<PixelData> originalFaceData = skyboxTex.allocBuffer(0, 0);
-                        for (UINT32 j = 0; j < 6; j++)
-                        {
-                            if (j < skyboxTex.getNumFaces())
-                                mSkyboxTexture->readData(*originalFaceData, 0, j);
-
-                            PixelUtil::scale(*originalFaceData, *scaledFaceData);
-
-                            mSkyboxFilteredReflections->writeData(*scaledFaceData, 0, j);
-                        }
-
-                        ReflectionProbes::filterCubemapForSpecular(mSkyboxFilteredReflections, scratchCubemap);
-                        ReflectionCubemapCache::instance().setCachedTexture(mSkybox->getUUID(), mSkyboxFilteredReflections);
-                    }
-                }
-            }
-            else
-                mSkyboxFilteredReflections = nullptr;
+			// Get reflections for skybox if needed/available
+			if (mSkybox != nullptr && mSkyboxTexture != nullptr)
+			{
+				// If haven't assigned them already, do it now
+				if (mSkyboxFilteredReflections == nullptr)
+				{
+					if (!ReflectionCubemapCache::instance().isDirty(mSkybox->getUUID()))
+						mSkyboxFilteredReflections = ReflectionCubemapCache::instance().getCachedTexture(mSkybox->getUUID());
+					else
+					{
+						mSkyboxFilteredReflections = Texture::create(cubemapDesc);
+
+						ReflectionProbes::scaleCubemap(mSkyboxTexture, 0, mSkyboxFilteredReflections, 0);
+						ReflectionProbes::filterCubemapForSpecular(mSkyboxFilteredReflections, scratchCubemap);
+						ReflectionCubemapCache::instance().setCachedTexture(mSkybox->getUUID(), mSkyboxFilteredReflections);
+					}
+				}
+			}
+			else
+				mSkyboxFilteredReflections = nullptr;
 
 
 		}
 		}
 		bs_frame_clear();
 		bs_frame_clear();
@@ -1406,7 +1379,7 @@ namespace bs { namespace ct
 		viewDesc.noLighting = false;
 		viewDesc.noLighting = false;
 		viewDesc.triggerCallbacks = false;
 		viewDesc.triggerCallbacks = false;
 		viewDesc.runPostProcessing = false;
 		viewDesc.runPostProcessing = false;
-        viewDesc.renderingReflections = true;
+		viewDesc.renderingReflections = true;
 
 
 		viewDesc.visibleLayers = 0xFFFFFFFFFFFFFFFF;
 		viewDesc.visibleLayers = 0xFFFFFFFFFFFFFFFF;
 		viewDesc.nearPlane = 0.5f;
 		viewDesc.nearPlane = 0.5f;