1#include "FourierTransform.h"
2#include "GPGPUQuadRenderer.h"
3#include "RenderContext.h"
5#include "Rendering/IEffects.h"
6#include "Rendering/IGraphicsDevice.h"
7#include "Rendering/ITextures.h"
8#include "Rendering/IRenderTargets.h"
9#include "Rendering/IBuffers.h"
10#include "Rendering/IContext.h"
12#include "Foundation/Logging/Logger.h"
23 const float twoPi = float(2.0*M_PI);
26 inline size_t reverseBits16(
size_t i,
size_t logN)
29 t = ((0xAAAAu & t) >> 1) | ((t << 1) & 0xAAAAu);
30 t = ((0xCCCCu & t) >> 2) | ((t << 2) & 0xCCCCu);
31 t = ((0xF0F0u & t) >> 4) | ((t << 4) & 0xF0F0u);
32 t = ((0xFF00u & t) >> 8) | ((t << 8) & 0xFF00u);
33 return t >> (16 - logN);
37 inline complex<float> twiddleFactor(
const int n,
const int N)
39 const float arg = (-twoPi*float(n)) /
float(N);
40 return complex<float>(std::cos(arg), std::sin(arg));
49Cogs::TextureHandle Cogs::FourierTransform2D::createBakedTable(IGraphicsDevice* device,
const int log2N)
53 std::vector<glm::vec4> table(log2N*N);
56 for (
int k = 0; k < N; k++) {
57 size_t iReversedIndex0 = reverseBits16(k &(~1), log2N);
58 size_t iReversedIndex1 = reverseBits16(k | 1, log2N);
59 bool iEven = (k & 1) == 0;
60 complex<float> iW = (iEven ? 1.f : -1.f);
61 table[k].x = float(iReversedIndex0);
62 table[k].y = float(iReversedIndex1);
63 table[k].z = iW.real();
64 table[k].w = iW.imag();
67 for (
int l = 1; l < log2N; l++) {
68 int blockSize = 2 << l;
69 int halfBlockSize = 1 << l;
70 int halfBlockMask = halfBlockSize - 1;
71 for (
int k = 0; k < N; k++) {
72 int iBlockNumber = k / blockSize;
73 int iBlockIndex = k & halfBlockMask;
74 bool iEven = (k & halfBlockSize) == 0;
75 int iTwiddleIndex = int(iBlockIndex << (log2N - l - 1));
76 int iIndex0 = blockSize*iBlockNumber + iBlockIndex;
77 int iIndex1 = iIndex0 + halfBlockSize;
78 complex<float> iW = (iEven ? 1.f : -1.f)*twiddleFactor(-iTwiddleIndex, N);
79 table[N*l + k].x = float(iIndex0);
80 table[N*l + k].y = float(iIndex1);
81 table[N*l + k].z = iW.real();
82 table[N*l + k].w = iW.imag();
86 return device->getTextures()->loadTexture(
reinterpret_cast<const unsigned char*
>(table.data()),
87 N, log2N, TextureFormat::R32G32B32A32_FLOAT, 0);
91void Cogs::FourierTransform2D::initialize(IGraphicsDevice* device, GPGPUQuadRenderer& gpgpuQuadRenderer)
93 this->device = device;
94 IEffects* effects = device->getEffects();
97 passRadix2x2 = effects->loadEffect(
"Terrain/GPGPUPassThroughVS.hlsl",
98 "Terrain/FourierTransformPS.hlsl",
101 LOG_ERROR(logger,
"Error loading passRadix2x2 effect.");
104 VertexFormatHandle handle = gpgpuQuadRenderer.vertexFormat();
105 passRadix2x2InputLayout = device->getBuffers()->loadInputLayout(&handle, 1, passRadix2x2);
107 definitions.push_back({
"DOUBLE",
"1" });
108 passRadix2x2Double = effects->loadEffect(
"Terrain/GPGPUPassThroughVS.hlsl",
109 "Terrain/FourierTransformPS.hlsl",
113 LOG_ERROR(logger,
"Error loading passRadix2x2Double effect.");
116 passRadix2x2DoubleInputLayout = device->getBuffers()->loadInputLayout(&handle, 1, passRadix2x2Double);
121void Cogs::FourierTransform2D::setSize(
int log2N)
125 tableTex = createBakedTable(device, log2N);
127 ITextures* textures = device->getTextures();
128 IRenderTargets* renderTargets = device->getRenderTargets();
130 for (
int k = 0; k < 2; k++) {
131 scratchTex[k] = textures->loadTexture(
nullptr, 1 << log2N, 1 << log2N, TextureFormat::R32G32_FLOAT,
TextureFlags::RenderTarget);
132 scratchTarget[k] = renderTargets->createRenderTarget(scratchTex[k]);
134 scratchDoubleTex[k] = textures->loadTexture(
nullptr, 1 << log2N, 1 << log2N, TextureFormat::R32G32B32A32_FLOAT,
TextureFlags::RenderTarget);
135 scratchDoubleTarget[k] = renderTargets->createRenderTarget(scratchDoubleTex[k]);
140void Cogs::FourierTransform2D::inverseFourierTransform(RenderContext& renderContext,
141 GPGPUQuadRenderer& gpgpuQuadRenderer,
142 RenderTargetHandle& target,
143 TextureHandle& source,
146 IContext* context = renderContext.context;
148 context->setViewport(0, 0,
float(1 << log2N),
float(1 << log2N));
149 context->setEffect(doubleData ? passRadix2x2Double : passRadix2x2);
151 for (
int pass = 0; pass < log2N; pass++) {
152 context->setRenderTarget(pass + 1 == log2N ? target : (doubleData ? scratchDoubleTarget[pass & 1] : scratchTarget[pass & 1]),
DepthStencilHandle::InvalidHandle);
153 gpgpuQuadRenderer.bind(context);
157 constants->fourierPass = pass;
160 context->setConstantBuffer(
"Globals", constantBuffer);
161 context->setTexture(
"bakedTable", 0, tableTex);
162 context->setTexture(
"source", 1, pass == 0 ? source : (doubleData ? scratchDoubleTex[(pass + 1) & 1] : scratchTex[(pass + 1) & 1]));
163 context->setInputLayout(doubleData ? passRadix2x2DoubleInputLayout : passRadix2x2InputLayout);
165 gpgpuQuadRenderer.draw(context);
Log implementation class.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
std::vector< PreprocessorDefinition > PreprocessorDefinitions
A set of preprocessor definitions.
@ Write
The buffer can be mapped and written to by the CPU after creation.
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
static const Handle_t NoHandle
Represents a handle to nothing.
static const Handle_t InvalidHandle
Represents an invalid handle.
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
@ RenderTarget
The texture can be used as a render target and drawn into.
@ Dynamic
Buffer will be loaded and modified with some frequency.