Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 109 additions & 109 deletions RayTracing/src/CPUBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void CPUBackend::Render(
m_ActiveScene = &scene;
m_ActiveCamera = &camera;
m_MaxBounces = maxBounces;

m_AccumFrameIndex = frameIndex;
if (scene.Version != m_LastBvhSceneVersion)
{
m_BVH.Build(scene);
Expand All @@ -62,71 +62,74 @@ void CPUBackend::Render(
);

#ifdef PN_ISPC
// ═══════════════════════════════════════════════
// ISPC-Accelerated Path (SIMD vectorized)
// Replaces the inner pixel loop with ISPC foreach
// ═══════════════════════════════════════════════
RenderISPC(camera, scene, outputBuffer, frameIndex);
#else
RenderCPUFallback(outputBuffer);
#endif
}

#ifdef PN_ISPC
void CPUBackend::RenderISPC(const Camera& camera, const Scene& scene, uint32_t* outputBuffer, uint32_t frameIndex)
{
const uint32_t width = m_Width;
const uint32_t height = m_Height;
const size_t pixelCount = static_cast<size_t>(width) * static_cast<size_t>(height);

const auto& rayDirs = camera.GetRayDirections();
const glm::vec3& camPos = camera.GetPosition();

// ── Pack ray directions into SoA flat arrays ──
m_ISPCRayDirX.resize(pixelCount);
m_ISPCRayDirY.resize(pixelCount);
m_ISPCRayDirZ.resize(pixelCount);
for (size_t i = 0; i < pixelCount; i++) {
m_ISPCRayDirX[i] = rayDirs[i].x;
m_ISPCRayDirY[i] = rayDirs[i].y;
m_ISPCRayDirZ[i] = rayDirs[i].z;
}

// ── Pack scene spheres (SoA layout) ──
const uint32_t sphereCount = static_cast<uint32_t>(scene.Spheres.size());
if (scene.Version != m_LastISPCSceneVersion)
{
const uint32_t width = m_Width;
const uint32_t height = m_Height;
const size_t pixelCount = static_cast<size_t>(width) * static_cast<size_t>(height);

const auto& rayDirs = camera.GetRayDirections();
const glm::vec3& camPos = camera.GetPosition();

// ── Pack ray directions into SoA flat arrays ──
m_ISPCRayDirX.resize(pixelCount);
m_ISPCRayDirY.resize(pixelCount);
m_ISPCRayDirZ.resize(pixelCount);
for (size_t i = 0; i < pixelCount; i++) {
m_ISPCRayDirX[i] = rayDirs[i].x;
m_ISPCRayDirY[i] = rayDirs[i].y;
m_ISPCRayDirZ[i] = rayDirs[i].z;
m_ISCPSphPosX.resize(sphereCount);
m_ISCPSphPosY.resize(sphereCount);
m_ISCPSphPosZ.resize(sphereCount);
m_ISCPSphRadius.resize(sphereCount);
m_ISCPSphMatIdx.resize(sphereCount);
for (uint32_t i = 0; i < sphereCount; i++) {
const auto& s = scene.Spheres[i];
m_ISCPSphPosX[i] = s.Position.x;
m_ISCPSphPosY[i] = s.Position.y;
m_ISCPSphPosZ[i] = s.Position.z;
m_ISCPSphRadius[i] = s.Radius;
m_ISCPSphMatIdx[i] = s.MaterialIndex;
}

// ── Pack scene spheres (SoA layout) ──
const uint32_t sphereCount = static_cast<uint32_t>(scene.Spheres.size());
if (scene.Version != m_LastISPCSceneVersion)
{
m_ISCPSphPosX.resize(sphereCount);
m_ISCPSphPosY.resize(sphereCount);
m_ISCPSphPosZ.resize(sphereCount);
m_ISCPSphRadius.resize(sphereCount);
m_ISCPSphMatIdx.resize(sphereCount);
for (uint32_t i = 0; i < sphereCount; i++) {
const auto& s = scene.Spheres[i];
m_ISCPSphPosX[i] = s.Position.x;
m_ISCPSphPosY[i] = s.Position.y;
m_ISCPSphPosZ[i] = s.Position.z;
m_ISCPSphRadius[i] = s.Radius;
m_ISCPSphMatIdx[i] = s.MaterialIndex;
}

// ── Pack materials (SoA layout) ──
const uint32_t matCount = static_cast<uint32_t>(scene.Materials.size());
m_ISPCMatAlbedoR.resize(matCount);
m_ISPCMatAlbedoG.resize(matCount);
m_ISPCMatAlbedoB.resize(matCount);
m_ISPCMatRoughness.resize(matCount);
m_ISPCMatMetallic.resize(matCount);
m_ISPCMatEmissionR.resize(matCount);
m_ISPCMatEmissionG.resize(matCount);
m_ISPCMatEmissionB.resize(matCount);
m_ISPCMatEmissionPower.resize(matCount);
for (uint32_t i = 0; i < matCount; i++) {
const auto& m = scene.Materials[i];
m_ISPCMatAlbedoR[i] = m.Albedo.x;
m_ISPCMatAlbedoG[i] = m.Albedo.y;
m_ISPCMatAlbedoB[i] = m.Albedo.z;
m_ISPCMatRoughness[i] = m.Roughness;
m_ISPCMatMetallic[i] = m.Metallic;
m_ISPCMatEmissionR[i] = m.EmissionColor.x;
m_ISPCMatEmissionG[i] = m.EmissionColor.y;
m_ISPCMatEmissionB[i] = m.EmissionColor.z;
m_ISPCMatEmissionPower[i] = m.EmissionPower;
}
m_LastISPCSceneVersion = scene.Version;
m_ISPCMatAlbedoR.resize(matCount);
m_ISPCMatAlbedoG.resize(matCount);
m_ISPCMatAlbedoB.resize(matCount);
m_ISPCMatRoughness.resize(matCount);
m_ISPCMatMetallic.resize(matCount);
m_ISPCMatEmissionR.resize(matCount);
m_ISPCMatEmissionG.resize(matCount);
m_ISPCMatEmissionB.resize(matCount);
m_ISPCMatEmissionPower.resize(matCount);
for (uint32_t i = 0; i < matCount; i++) {
const auto& m = scene.Materials[i];
m_ISPCMatAlbedoR[i] = m.Albedo.x;
m_ISPCMatAlbedoG[i] = m.Albedo.y;
m_ISPCMatAlbedoB[i] = m.Albedo.z;
m_ISPCMatRoughness[i] = m.Roughness;
m_ISPCMatMetallic[i] = m.Metallic;
m_ISPCMatEmissionR[i] = m.EmissionColor.x;
m_ISPCMatEmissionG[i] = m.EmissionColor.y;
m_ISPCMatEmissionB[i] = m.EmissionColor.z;
m_ISPCMatEmissionPower[i] = m.EmissionPower;
}
m_LastISPCSceneVersion = scene.Version;

// ── Pack BVH (SoA layout) ──
{
Expand Down Expand Up @@ -156,58 +159,60 @@ void CPUBackend::Render(

m_ISPCBvhSphereIndices.assign(bvhSpIndices.begin(), bvhSpIndices.end());
}
}

// ── Output buffers ──
m_ISPCOutputR.resize(pixelCount);
m_ISPCOutputG.resize(pixelCount);
m_ISPCOutputB.resize(pixelCount);
m_ISPCOutputA.resize(pixelCount);

// ── Call ISPC kernel ──
ispc::ISPCRenderPixels(
camPos.x, camPos.y, camPos.z,
m_ISPCRayDirX.data(), m_ISPCRayDirY.data(), m_ISPCRayDirZ.data(),
m_ISCPSphPosX.data(), m_ISCPSphPosY.data(), m_ISCPSphPosZ.data(),
m_ISCPSphRadius.data(), m_ISCPSphMatIdx.data(),
m_ISPCMatAlbedoR.data(), m_ISPCMatAlbedoG.data(), m_ISPCMatAlbedoB.data(),
m_ISPCMatRoughness.data(), m_ISPCMatMetallic.data(),
m_ISPCMatEmissionR.data(), m_ISPCMatEmissionG.data(), m_ISPCMatEmissionB.data(),
m_ISPCMatEmissionPower.data(),
m_ISPCOutputR.data(), m_ISPCOutputG.data(), m_ISPCOutputB.data(), m_ISPCOutputA.data(),
static_cast<int32_t>(pixelCount), static_cast<int32_t>(sphereCount),
// BVH data
m_ISPCBvhMinX.data(), m_ISPCBvhMinY.data(), m_ISPCBvhMinZ.data(),
m_ISPCBvhMaxX.data(), m_ISPCBvhMaxY.data(), m_ISPCBvhMaxZ.data(),
m_ISPCBvhLeftFirst.data(), m_ISPCBvhCount.data(),
m_ISPCBvhSphereIndices.data(),
static_cast<int32_t>(m_ISPCBvhLeftFirst.size()),
static_cast<int32_t>(frameIndex), maxBounces
);

// ── Unpack: accumulate + tone map + RGBA convert ──
for (size_t i = 0; i < pixelCount; i++) {
glm::vec4 color(m_ISPCOutputR[i], m_ISPCOutputG[i], m_ISPCOutputB[i], m_ISPCOutputA[i]);
m_AccumulationData[i] += color;

glm::vec4 accumulated = m_AccumulationData[i];
accumulated /= static_cast<float>(frameIndex);
accumulated = glm::clamp(accumulated, glm::vec4(0.0f), glm::vec4(1.0f));
outputBuffer[i] = PathTracerCore::ConvertToRGBA(accumulated);
}
// ── Output buffers ──
m_ISPCOutputR.resize(pixelCount);
m_ISPCOutputG.resize(pixelCount);
m_ISPCOutputB.resize(pixelCount);
m_ISPCOutputA.resize(pixelCount);

// ── Call ISPC kernel ──
ispc::ISPCRenderPixels(
camPos.x, camPos.y, camPos.z,
m_ISPCRayDirX.data(), m_ISPCRayDirY.data(), m_ISPCRayDirZ.data(),
m_ISCPSphPosX.data(), m_ISCPSphPosY.data(), m_ISCPSphPosZ.data(),
m_ISCPSphRadius.data(), m_ISCPSphMatIdx.data(),
m_ISPCMatAlbedoR.data(), m_ISPCMatAlbedoG.data(), m_ISPCMatAlbedoB.data(),
m_ISPCMatRoughness.data(), m_ISPCMatMetallic.data(),
m_ISPCMatEmissionR.data(), m_ISPCMatEmissionG.data(), m_ISPCMatEmissionB.data(),
m_ISPCMatEmissionPower.data(),
m_ISPCOutputR.data(), m_ISPCOutputG.data(), m_ISPCOutputB.data(), m_ISPCOutputA.data(),
static_cast<int32_t>(pixelCount), static_cast<int32_t>(sphereCount),
// BVH data
m_ISPCBvhMinX.data(), m_ISPCBvhMinY.data(), m_ISPCBvhMinZ.data(),
m_ISPCBvhMaxX.data(), m_ISPCBvhMaxY.data(), m_ISPCBvhMaxZ.data(),
m_ISPCBvhLeftFirst.data(), m_ISPCBvhCount.data(),
m_ISPCBvhSphereIndices.data(),
static_cast<int32_t>(m_ISPCBvhLeftFirst.size()),
static_cast<int32_t>(frameIndex), m_MaxBounces
);

// ── Unpack: accumulate + tone map + RGBA convert ──
for (size_t i = 0; i < pixelCount; i++) {
glm::vec4 color(m_ISPCOutputR[i], m_ISPCOutputG[i], m_ISPCOutputB[i], m_ISPCOutputA[i]);
m_AccumulationData[i] += color;

glm::vec4 accumulated = m_AccumulationData[i];
accumulated /= static_cast<float>(frameIndex);
accumulated = glm::clamp(accumulated, glm::vec4(0.0f), glm::vec4(1.0f));
outputBuffer[i] = PathTracerCore::ConvertToRGBA(accumulated);
}
#else
// ── C++ Scalar / std::execution::par Fallback ──
}
#endif

void CPUBackend::RenderCPUFallback(uint32_t* outputBuffer)
{
static constexpr bool kMultithreaded = true;
m_AccumFrameIndex = frameIndex;
if constexpr (kMultithreaded) {
std::for_each(
std::execution::par,
m_ImageVerticalIterator.begin(), m_ImageVerticalIterator.end(),
[this](uint32_t y)
[this, outputBuffer](uint32_t y)
{
std::ranges::for_each(
m_ImageHorizontalIterator.begin(), m_ImageHorizontalIterator.end(),
[this, y, width = m_Width](const uint32_t x)
[this, y, outputBuffer, width = m_Width](const uint32_t x)
{
const glm::vec4 color = PerPixel(x, y);
const size_t idx = static_cast<size_t>(x) + static_cast<size_t>(y) * static_cast<size_t>(width);
Expand All @@ -217,12 +222,8 @@ void CPUBackend::Render(
glm::vec4 accumulatedColor = m_AccumulationData[idx];
accumulatedColor /= static_cast<float>(m_AccumFrameIndex);

accumulatedColor = glm::clamp(
accumulatedColor,
glm::vec4(0.0f), glm::vec4(1.0f)
);
outputBuffer[idx]
= PathTracerCore::ConvertToRGBA(accumulatedColor);
accumulatedColor = glm::clamp(accumulatedColor, glm::vec4(0.0f), glm::vec4(1.0f));
outputBuffer[idx] = PathTracerCore::ConvertToRGBA(accumulatedColor);
});
});
} else {
Expand All @@ -240,7 +241,6 @@ void CPUBackend::Render(
}
}
}
#endif // PN_ISPC
}

// ──────────────────────────────────────────────
Expand Down
5 changes: 5 additions & 0 deletions RayTracing/src/CPUBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ class CPUBackend : public IRenderBackend
) const noexcept;
static HitPayLoad Miss(const Ray& ray) noexcept;

void RenderCPUFallback(uint32_t* outputBuffer);
#ifdef PN_ISPC
void RenderISPC(const Camera& camera, const Scene& scene, uint32_t* outputBuffer, uint32_t frameIndex);
#endif

private:
uint32_t m_Width = 0;
uint32_t m_Height = 0;
Expand Down
Loading
Loading