Cogs.Core
FourierTransform.cpp
1#include "FourierTransform.h"
2#include "GPGPUQuadRenderer.h"
3#include "RenderContext.h"
4
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"
11
12#include "Foundation/Logging/Logger.h"
13
14#include <cmath>
15#include <complex>
16
17using std::complex;
18
19namespace
20{
21 Cogs::Logging::Log logger = Cogs::Logging::getLogger("FourierTransform");
22
23 const float twoPi = float(2.0*M_PI);
24
25 // Reverse the lower logN bits of i
26 inline size_t reverseBits16(size_t i, size_t logN)
27 {
28 size_t t = i;
29 t = ((0xAAAAu & t) >> 1) | ((t << 1) & 0xAAAAu); // 1010101010101010
30 t = ((0xCCCCu & t) >> 2) | ((t << 2) & 0xCCCCu); // 1100110011001100
31 t = ((0xF0F0u & t) >> 4) | ((t << 4) & 0xF0F0u); // 1111000011110000
32 t = ((0xFF00u & t) >> 8) | ((t << 8) & 0xFF00u); // 1111111100000000
33 return t >> (16 - logN);
34 }
35
36 // Calculate complex twiddle-factor e^{-2\pi n/N}
37 inline complex<float> twiddleFactor(const int n, const int N)
38 {
39 const float arg = (-twoPi*float(n)) / float(N);
40 return complex<float>(std::cos(arg), std::sin(arg));
41 }
42
43 struct Globals{
44 int fourierPass;
45 };
46
47} // of anonymous namespace
48
49Cogs::TextureHandle Cogs::FourierTransform2D::createBakedTable(IGraphicsDevice* device, const int log2N)
50{
51 int N = 1 << log2N;
52
53 std::vector<glm::vec4> table(log2N*N);
54
55 // First pass with permutations
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();
65 }
66 // Subsequent passes
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();
83 }
84 }
85
86 return device->getTextures()->loadTexture(reinterpret_cast<const unsigned char*>(table.data()),
87 N, log2N, TextureFormat::R32G32B32A32_FLOAT, 0);
88
89}
90
91void Cogs::FourierTransform2D::initialize(IGraphicsDevice* device, GPGPUQuadRenderer& gpgpuQuadRenderer)
92{
93 this->device = device;
94 IEffects* effects = device->getEffects();
95
96 PreprocessorDefinitions definitions;
97 passRadix2x2 = effects->loadEffect("Terrain/GPGPUPassThroughVS.hlsl",
98 "Terrain/FourierTransformPS.hlsl",
99 definitions);
100 if (!HandleIsValid(passRadix2x2)) {
101 LOG_ERROR(logger, "Error loading passRadix2x2 effect.");
102 return;
103 }
104 VertexFormatHandle handle = gpgpuQuadRenderer.vertexFormat();
105 passRadix2x2InputLayout = device->getBuffers()->loadInputLayout(&handle, 1, passRadix2x2);
106
107 definitions.push_back({ "DOUBLE", "1" });
108 passRadix2x2Double = effects->loadEffect("Terrain/GPGPUPassThroughVS.hlsl",
109 "Terrain/FourierTransformPS.hlsl",
110 definitions);
111
112 if (!HandleIsValid(passRadix2x2Double)) {
113 LOG_ERROR(logger, "Error loading passRadix2x2Double effect.");
114 return;
115 }
116 passRadix2x2DoubleInputLayout = device->getBuffers()->loadInputLayout(&handle, 1, passRadix2x2Double);
117
118 constantBuffer = device->getBuffers()->loadBuffer(nullptr, sizeof(int), Usage::Dynamic, AccessMode::Write, BindFlags::ConstantBuffer);
119}
120
121void Cogs::FourierTransform2D::setSize(int log2N)
122{
123 this->log2N = log2N;
124
125 tableTex = createBakedTable(device, log2N);
126
127 ITextures* textures = device->getTextures();
128 IRenderTargets* renderTargets = device->getRenderTargets();
129
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]);
133
134 scratchDoubleTex[k] = textures->loadTexture(nullptr, 1 << log2N, 1 << log2N, TextureFormat::R32G32B32A32_FLOAT, TextureFlags::RenderTarget);
135 scratchDoubleTarget[k] = renderTargets->createRenderTarget(scratchDoubleTex[k]);
136 }
137}
138
139
140void Cogs::FourierTransform2D::inverseFourierTransform(RenderContext& renderContext,
141 GPGPUQuadRenderer& gpgpuQuadRenderer,
142 RenderTargetHandle& target,
143 TextureHandle& source,
144 bool doubleData)
145{
146 IContext* context = renderContext.context;
147
148 context->setViewport(0, 0, float(1 << log2N), float(1 << log2N));
149 context->setEffect(doubleData ? passRadix2x2Double : passRadix2x2);
150
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);
154 {
155 MappedBuffer<Globals> constants(context, constantBuffer, MapMode::WriteDiscard);
156 if (constants) {
157 constants->fourierPass = pass;
158 }
159 }
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);
164
165 gpgpuQuadRenderer.draw(context);
166 }
167
169}
Log implementation class.
Definition: LogManager.h:140
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
Definition: LogManager.h:181
std::vector< PreprocessorDefinition > PreprocessorDefinitions
A set of preprocessor definitions.
Definition: IEffects.h:13
@ Write
The buffer can be mapped and written to by the CPU after creation.
Definition: Flags.h:50
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
Definition: Flags.h:72
static const Handle_t NoHandle
Represents a handle to nothing.
Definition: Common.h:78
static const Handle_t InvalidHandle
Represents an invalid handle.
Definition: Common.h:81
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
Definition: Flags.h:103
@ RenderTarget
The texture can be used as a render target and drawn into.
Definition: Flags.h:120
@ Dynamic
Buffer will be loaded and modified with some frequency.
Definition: Flags.h:30