initial commit

This commit is contained in:
Tanishq Dubey 2025-04-07 20:08:16 -04:00
parent a8155a2d85
commit 79047a133f
3 changed files with 147 additions and 234 deletions

View File

@ -261,26 +261,6 @@ Pos=290,135
Size=700,450 Size=700,450
Collapsed=0 Collapsed=0
[Window][Open Image File##filebrowser_107578099843392]
Pos=290,135
Size=700,450
Collapsed=0
[Window][Open Image File##filebrowser_107014633521472]
Pos=290,135
Size=700,450
Collapsed=0
[Window][Open Image File##filebrowser_94338082222400]
Pos=290,135
Size=700,450
Collapsed=0
[Window][Open Image File##filebrowser_101441870037440]
Pos=290,135
Size=700,450
Collapsed=0
[Docking][Data] [Docking][Data]
DockSpace ID=0xE098E157 Window=0x9E772337 Pos=0,19 Size=3840,2072 Split=X DockSpace ID=0xE098E157 Window=0x9E772337 Pos=0,19 Size=3840,2072 Split=X
DockNode ID=0x00000001 Parent=0xE098E157 SizeRef=1641,720 Split=X DockNode ID=0x00000001 Parent=0xE098E157 SizeRef=1641,720 Split=X

341
main.cpp
View File

@ -640,18 +640,13 @@ static ImVec4 g_cropRectNormInitial = g_cropRectNorm; // Store initial
static float g_cropAspectRatio = 0.0f; // 0.0f = Freeform, > 0.0f = constrained (Width / Height) static float g_cropAspectRatio = 0.0f; // 0.0f = Freeform, > 0.0f = constrained (Width / Height)
static int g_selectedAspectRatioIndex = 0; // Index for the dropdown static int g_selectedAspectRatioIndex = 0; // Index for the dropdown
static GLuint g_histogramTFShader = 0; // Program with VS+GS static GLuint g_histogramComputeShader = 0;
static GLuint g_histogramTFBuffer = 0; // Buffer to store bin indices from TF static GLuint g_histogramSSBO = 0;
static GLuint g_histogramTFQuery = 0; // Query object to count primitives written const int NUM_HISTOGRAM_BINS = 256;
static GLuint g_histogramTFVAO = 0; // Dummy VAO for drawing points const int HISTOGRAM_BUFFER_SIZE = NUM_HISTOGRAM_BINS * 3; // R, G, B
static size_t g_histogramTFBufferSize = 0; // Size in bytes static std::vector<unsigned int> g_histogramDataCPU(HISTOGRAM_BUFFER_SIZE, 0);
static std::vector<float> g_histogramTFDataCPU; // CPU buffer for readback (float) static unsigned int g_histogramMaxCount = 255; // Max count found, for scaling (init to 1 to avoid div by zero)
// Keep the final histogram count data static bool g_histogramResourcesInitialized = false;
const int NUM_HISTOGRAM_BINS_TF = 256; // Use separate const if needed
const int HISTOGRAM_BUFFER_SIZE_TF = NUM_HISTOGRAM_BINS_TF * 3;
static std::vector<unsigned int> g_histogramCountsCPU(HISTOGRAM_BUFFER_SIZE_TF, 0);
static unsigned int g_histogramMaxCountTF = 1;
static bool g_histogramTFResourcesInitialized = false;
// Interaction state // Interaction state
enum class CropHandle enum class CropHandle
@ -672,91 +667,75 @@ static bool g_isDraggingCrop = false;
static ImVec2 g_dragStartMousePos = ImVec2(0, 0); // Screen coords static ImVec2 g_dragStartMousePos = ImVec2(0, 0); // Screen coords
bool InitHistogramTFResources(const std::string& shaderBasePath) { bool InitHistogramResources(const std::string& shaderBasePath) {
printf("Initializing Histogram Transform Feedback Resources...\n"); printf("Initializing Histogram Resources...\n");
g_histogramTFResourcesInitialized = false; // Assume failure until success // Load Compute Shader
// We need a way to load compute shaders, modify shader_utils or add here
// 1. Load Vertex and Geometry Shaders std::string compSource = ReadFile(shaderBasePath + "histogram.comp"); // Assuming ReadFile exists
std::string vsSource = ReadFile(shaderBasePath + "histogram_tf.vert"); if (compSource.empty()) {
std::string gsSource = ReadFile(shaderBasePath + "histogram_tf.geom"); fprintf(stderr, "ERROR: Failed to read histogram.comp\n");
if (vsSource.empty() || gsSource.empty()) { return false;
fprintf(stderr, "ERROR: Failed to read histogram_tf shaders.\n"); }
// Simple Compute Shader Compilation/Linking (add error checking!)
GLuint computeShaderObj = glCreateShader(GL_COMPUTE_SHADER);
const char* src = compSource.c_str();
glShaderSource(computeShaderObj, 1, &src, nullptr);
glCompileShader(computeShaderObj);
// --- Add GLint success; glGetShaderiv; glGetShaderInfoLog checks ---
GLint success;
glGetShaderiv(computeShaderObj, GL_COMPILE_STATUS, &success);
if (!success) {
GLint logLength;
glGetShaderiv(computeShaderObj, GL_INFO_LOG_LENGTH, &logLength);
std::vector<char> log(logLength);
glGetShaderInfoLog(computeShaderObj, logLength, nullptr, log.data());
fprintf(stderr, "ERROR::SHADER::HISTOGRAM::COMPILATION_FAILED\n%s\n", log.data());
glDeleteShader(computeShaderObj);
return false; return false;
} }
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource); // Assuming CompileShader exists
GLuint gs = CompileShader(GL_GEOMETRY_SHADER, gsSource); g_histogramComputeShader = glCreateProgram();
if (!vs || !gs) { glAttachShader(g_histogramComputeShader, computeShaderObj);
if(vs) glDeleteShader(vs); glLinkProgram(g_histogramComputeShader);
if(gs) glDeleteShader(gs); // --- Add GLint success; glGetProgramiv; glGetProgramInfoLog checks ---
glGetProgramiv(g_histogramComputeShader, GL_LINK_STATUS, &success);
if (!success) {
GLint logLength;
glGetProgramiv(g_histogramComputeShader, GL_INFO_LOG_LENGTH, &logLength);
std::vector<char> log(logLength);
glGetProgramInfoLog(g_histogramComputeShader, logLength, nullptr, log.data());
fprintf(stderr, "ERROR::PROGRAM::HISTOGRAM::LINKING_FAILED\n%s\n", log.data());
glDeleteProgram(g_histogramComputeShader);
g_histogramComputeShader = 0;
glDeleteShader(computeShaderObj); // Delete shader obj even on link failure
return false; return false;
} }
// 2. Create and Link Shader Program
g_histogramTFShader = glCreateProgram();
glAttachShader(g_histogramTFShader, vs);
glAttachShader(g_histogramTFShader, gs);
// 3. Specify Transform Feedback Varying *** BEFORE LINKING *** glDeleteShader(computeShaderObj); // Delete shader object after linking
const char* varyings[] = { "tf_BinIndex" }; // Must match 'out' variable in GS printf("Histogram compute shader loaded and linked successfully (Program ID: %u).\n", g_histogramComputeShader);
glTransformFeedbackVaryings(g_histogramTFShader, 1, varyings, GL_INTERLEAVED_ATTRIBS); // Or GL_SEPARATE_ATTRIBS if needed
glLinkProgram(g_histogramTFShader);
// --- Add linking error checks (glGetProgramiv, glGetProgramInfoLog) --- // Create Shader Storage Buffer Object (SSBO)
GLint linkSuccess; glGenBuffers(1, &g_histogramSSBO);
glGetProgramiv(g_histogramTFShader, GL_LINK_STATUS, &linkSuccess); glBindBuffer(GL_SHADER_STORAGE_BUFFER, g_histogramSSBO);
if (!linkSuccess) { // Allocate buffer size: 3 channels * 256 bins * size of uint
// ... get and print link error log ... glBufferData(GL_SHADER_STORAGE_BUFFER, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int), NULL, GL_DYNAMIC_READ); // Data will be written by GPU, read by CPU
fprintf(stderr, "ERROR: Failed to link histogram_tf shader program.\n"); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind
glDeleteProgram(g_histogramTFShader); g_histogramTFShader = 0;
glDeleteShader(vs); glDeleteShader(gs); GLenum err = glGetError();
if (err != GL_NO_ERROR || g_histogramSSBO == 0) {
fprintf(stderr, "ERROR: Failed to create histogram SSBO. OpenGL Error: %u\n", err);
if (g_histogramComputeShader) glDeleteProgram(g_histogramComputeShader);
g_histogramComputeShader = 0;
return false; return false;
} else {
printf("Histogram SSBO created successfully (Buffer ID: %u, Size: %d bytes).\n", g_histogramSSBO, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int));
} }
printf("Histogram TF shader linked successfully (Program ID: %u).\n", g_histogramTFShader);
// Detach and delete shaders after linking
glDetachShader(g_histogramTFShader, vs);
glDetachShader(g_histogramTFShader, gs);
glDeleteShader(vs);
glDeleteShader(gs);
// 4. Create Transform Feedback Buffer g_histogramResourcesInitialized = true;
// Size needs to accommodate width * height * 3 floats (one index per channel per pixel)
// We allocate dynamically later when image size is known, or create large enough buffer here.
// Let's just create the ID now. Buffer allocation will happen in Compute function.
glGenBuffers(1, &g_histogramTFBuffer);
if(g_histogramTFBuffer == 0) {
fprintf(stderr, "ERROR: Failed to generate histogram TF buffer.\n");
glDeleteProgram(g_histogramTFShader); g_histogramTFShader = 0;
return false;
}
printf("Histogram TF buffer generated (ID: %u).\n", g_histogramTFBuffer);
// 5. Create Query Object for primitives written (optional but good)
glGenQueries(1, &g_histogramTFQuery);
if(g_histogramTFQuery == 0) {
fprintf(stderr, "ERROR: Failed to generate histogram TF query.\n");
glDeleteBuffers(1, &g_histogramTFBuffer); g_histogramTFBuffer = 0;
glDeleteProgram(g_histogramTFShader); g_histogramTFShader = 0;
return false;
}
printf("Histogram TF query generated (ID: %u).\n", g_histogramTFQuery);
// 6. Create Dummy VAO (needed to initiate drawing for TF)
glGenVertexArrays(1, &g_histogramTFVAO);
if(g_histogramTFVAO == 0) {
fprintf(stderr, "ERROR: Failed to generate histogram TF VAO.\n");
glDeleteQueries(1, &g_histogramTFQuery); g_histogramTFQuery = 0;
glDeleteBuffers(1, &g_histogramTFBuffer); g_histogramTFBuffer = 0;
glDeleteProgram(g_histogramTFShader); g_histogramTFShader = 0;
return false;
}
printf("Histogram TF VAO generated (ID: %u).\n", g_histogramTFVAO);
g_histogramTFResourcesInitialized = true;
return true; return true;
} }
@ -1095,127 +1074,96 @@ void InitShaderOperations(const std::string &shaderBasePath)
// Add this function somewhere accessible, e.g., before main() // Add this function somewhere accessible, e.g., before main()
void ComputeHistogramTF(GLuint linearTextureID, int width, int height) { void ComputeHistogramGPU(GLuint inputTextureID, int width, int height) {
if (!g_histogramTFResourcesInitialized || linearTextureID == 0 || width <= 0 || height <= 0) { if (!g_histogramResourcesInitialized || inputTextureID == 0 || width <= 0 || height <= 0) {
std::fill(g_histogramCountsCPU.begin(), g_histogramCountsCPU.end(), 0); // Clear CPU data if not computed
g_histogramMaxCountTF = 1; std::fill(g_histogramDataCPU.begin(), g_histogramDataCPU.end(), 0);
g_histogramMaxCount = 1;
printf("Histogram resources not initialized or invalid input. Skipping computation.\n");
return; return;
} }
size_t requiredFloats = static_cast<size_t>(width) * height * 3; // 1. Clear the SSBO buffer data to zeros
size_t requiredBytes = requiredFloats * sizeof(float); glBindBuffer(GL_SHADER_STORAGE_BUFFER, g_histogramSSBO);
// Using glBufferSubData might be marginally faster than glClearBufferData if driver optimizes zeroing
// 1. Ensure TF Buffer is large enough // static std::vector<unsigned int> zeros(HISTOGRAM_BUFFER_SIZE, 0); // Create once
if (requiredBytes > g_histogramTFBufferSize) { // glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int), zeros.data());
printf("Resizing Histogram TF buffer to %zu bytes\n", requiredBytes); // Or use glClearBufferData (often recommended)
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, g_histogramTFBuffer); GLuint zero = 0;
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, requiredBytes, NULL, GL_DYNAMIC_READ); // Resize/allocate glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &zero);
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
fprintf(stderr, "ERROR: Failed to resize histogram TF buffer. GL Error: %u\n", err);
return; // Cannot proceed
}
g_histogramTFBufferSize = requiredBytes;
g_histogramTFDataCPU.resize(requiredFloats); // Resize CPU buffer too
} else {
// Ensure CPU buffer is correct size even if GPU buffer wasn't resized
if (g_histogramTFDataCPU.size() != requiredFloats) {
g_histogramTFDataCPU.resize(requiredFloats);
}
}
// 2. Setup Transform Feedback State // 2. Bind resources and dispatch compute shader
glUseProgram(g_histogramTFShader); glUseProgram(g_histogramComputeShader);
// Bind the input texture (Linear Float format) // Bind input texture as image unit 0 (read-only)
glActiveTexture(GL_TEXTURE0); // Use texture unit 0 // IMPORTANT: Ensure the format matches the compute shader layout qualifier (e.g., rgba8)
glBindTexture(GL_TEXTURE_2D, linearTextureID); // If textureToDisplay is RGBA16F, you'd use layout(rgba16f) in shader
glUniform1i(glGetUniformLocation(g_histogramTFShader, "InputTexture"), 0); // Tell shader sampler is on unit 0 glBindImageTexture(0, inputTextureID, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8); // Assuming display texture is RGBA8
// Disable rasterization - we only care about the TF output // Bind SSBO to binding point 1
glEnable(GL_RASTERIZER_DISCARD); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, g_histogramSSBO);
// Bind the buffer for transform feedback output // Calculate number of work groups
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, g_histogramTFBuffer); // Binding index 0 GLuint workGroupSizeX = 16; // Must match layout in shader
GLuint workGroupSizeY = 16;
GLuint numGroupsX = (width + workGroupSizeX - 1) / workGroupSizeX;
GLuint numGroupsY = (height + workGroupSizeY - 1) / workGroupSizeY;
// 3. Begin Transform Feedback and Query // Dispatch the compute shader
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, g_histogramTFQuery); glDispatchCompute(numGroupsX, numGroupsY, 1);
glBeginTransformFeedback(GL_POINTS); // Outputting points from GS
// 4. Draw Points (one per pixel) - using dummy VAO // 3. Synchronization: Ensure compute shader writes finish before CPU reads buffer
glBindVertexArray(g_histogramTFVAO); // Use a memory barrier on the SSBO writes
glDrawArrays(GL_POINTS, 0, width * height); // Draw one point for each pixel glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glBindVertexArray(0);
// 5. End Transform Feedback and Query // Unbind resources (optional here, but good practice)
glEndTransformFeedback(); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
// Unbind TF buffer
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
// Re-enable rasterization
glDisable(GL_RASTERIZER_DISCARD);
// Unbind texture and program
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0); glUseProgram(0);
// Optional: Get number of primitives written from query // 4. Read histogram data back from SSBO to CPU vector
// GLuint primitivesWritten = 0; glBindBuffer(GL_SHADER_STORAGE_BUFFER, g_histogramSSBO);
// glGetQueryObjectuiv(g_histogramTFQuery, GL_QUERY_RESULT, &primitivesWritten); glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int), g_histogramDataCPU.data());
// printf("TF Primitives Written: %u (Expected: %d)\n", primitivesWritten, width * height * 3); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind
// 6. Read back the Transform Feedback buffer // 5. Find the maximum count for scaling the plot (optional, can be capped)
// Make sure TF operations are finished. glFlush/glFinish might be needed g_histogramMaxCount = 255; // Reset to 255 (prevents div by zero)
// depending on driver, but often implicitly synced by readback. Add if needed. for (unsigned int count : g_histogramDataCPU) {
// glFlush(); if (count > g_histogramMaxCount) {
glBindBuffer(GL_ARRAY_BUFFER, g_histogramTFBuffer); // Bind to generic target for readback g_histogramMaxCount = count;
glGetBufferSubData(GL_ARRAY_BUFFER, 0, requiredBytes, g_histogramTFDataCPU.data());
glBindBuffer(GL_ARRAY_BUFFER, 0);
GLenum readErr = glGetError();
if (readErr != GL_NO_ERROR) {
fprintf(stderr, "OpenGL Error during histogram TF readback: %u\n", readErr);
std::fill(g_histogramCountsCPU.begin(), g_histogramCountsCPU.end(), 0);
g_histogramMaxCountTF = 1;
return;
}
// 7. Process the readback data CPU-side to build histogram counts
std::fill(g_histogramCountsCPU.begin(), g_histogramCountsCPU.end(), 0); // Clear counts
g_histogramMaxCountTF = 1;
for (float binIndexF : g_histogramTFDataCPU) {
// Convert float index back to uint, handle potential slight inaccuracies
int binIndex = static_cast<int>(round(binIndexF));
// Check bounds (0 to 767)
if (binIndex >= 0 && binIndex < HISTOGRAM_BUFFER_SIZE_TF) {
g_histogramCountsCPU[binIndex]++;
if (g_histogramCountsCPU[binIndex] > g_histogramMaxCountTF) {
g_histogramMaxCountTF = g_histogramCountsCPU[binIndex];
}
} }
// else { printf("Warning: Out of bounds TF bin index: %d\n", binIndex); } // Debugging
} }
// if (g_histogramMaxCountTF == 1) { // Optional: Cap max count to prevent extreme peaks from flattening the rest
// printf("Warning: Max histogram count is still 1 after TF processing.\n"); // unsigned int capThreshold = (width * height) / 50; // e.g., cap at 2% of pixels
// } // g_histogramMaxCount = std::min(g_histogramMaxCount, capThreshold);
// if (g_histogramMaxCount == 0) g_histogramMaxCount = 1; // Ensure not zero after capping
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
fprintf(stderr, "OpenGL Error during histogram computation/readback: %u\n", err);
// Optionally clear CPU data on error
std::fill(g_histogramDataCPU.begin(), g_histogramDataCPU.end(), 0);
g_histogramMaxCount = 1;
printf("Histogram computation failed. Data cleared.\n");
}
else {
printf("Histogram computed. Max count: %u\n", g_histogramMaxCount);
}
} }
// Add this function somewhere accessible, e.g., before main() // Add this function somewhere accessible, e.g., before main()
void DrawHistogramWidget(const char* widgetId, ImVec2 graphSize) { void DrawHistogramWidget(const char* widgetId, ImVec2 graphSize) {
if (g_histogramCountsCPU.empty() || g_histogramMaxCountTF <= 1) { // Check if data is valid if (g_histogramDataCPU.empty() || g_histogramMaxCount <= 1) { // Check if data is valid
if (g_histogramCountsCPU.empty()) { if (g_histogramDataCPU.empty()) {
ImGui::Text("Histogram data not initialized."); ImGui::Text("Histogram data not initialized.");
} else { } else {
ImGui::Text("Histogram data is empty or invalid."); ImGui::Text("Histogram data is empty or invalid.");
} }
if (g_histogramMaxCountTF <= 1) { if (g_histogramMaxCount <= 1) {
ImGui::Text("Histogram max count is invalid."); ImGui::Text("Histogram max count is invalid.");
} }
ImGui::Text("Histogram data not available."); ImGui::Text("Histogram data not available.");
@ -1235,8 +1183,8 @@ void DrawHistogramWidget(const char* widgetId, ImVec2 graphSize) {
drawList->AddRectFilled(widgetPos, widgetPos + graphSize, IM_COL32(30, 30, 30, 200)); drawList->AddRectFilled(widgetPos, widgetPos + graphSize, IM_COL32(30, 30, 30, 200));
// Calculate scaling factors // Calculate scaling factors
float barWidth = graphSize.x / float(NUM_HISTOGRAM_BINS_TF); float barWidth = graphSize.x / float(NUM_HISTOGRAM_BINS);
float scaleY = graphSize.y / float(g_histogramMaxCountTF); float scaleY = graphSize.y / float(g_histogramMaxCount); // Scale based on max count
// Define colors (with some transparency for overlap visibility) // Define colors (with some transparency for overlap visibility)
const ImU32 colR = IM_COL32(255, 0, 0, 180); const ImU32 colR = IM_COL32(255, 0, 0, 180);
@ -1244,11 +1192,11 @@ void DrawHistogramWidget(const char* widgetId, ImVec2 graphSize) {
const ImU32 colB = IM_COL32(0, 0, 255, 180); const ImU32 colB = IM_COL32(0, 0, 255, 180);
// Draw the histogram bars (R, G, B) // Draw the histogram bars (R, G, B)
for (int i = 0; i < NUM_HISTOGRAM_BINS_TF; ++i) { for (int i = 0; i < NUM_HISTOGRAM_BINS; ++i) {
// Get heights from g_histogramCountsCPU // Get heights (clamped to graph size)
float hR = ImMin(float(g_histogramCountsCPU[i]) * scaleY, graphSize.y); float hR = ImMin(float(g_histogramDataCPU[i]) * scaleY, graphSize.y);
float hG = ImMin(float(g_histogramCountsCPU[i + NUM_HISTOGRAM_BINS_TF]) * scaleY, graphSize.y); float hG = ImMin(float(g_histogramDataCPU[i + NUM_HISTOGRAM_BINS]) * scaleY, graphSize.y);
float hB = ImMin(float(g_histogramCountsCPU[i + NUM_HISTOGRAM_BINS_TF * 2]) * scaleY, graphSize.y); float hB = ImMin(float(g_histogramDataCPU[i + NUM_HISTOGRAM_BINS * 2]) * scaleY, graphSize.y);
// Calculate bar positions // Calculate bar positions
float x0 = widgetPos.x + float(i) * barWidth; float x0 = widgetPos.x + float(i) * barWidth;
@ -1410,7 +1358,7 @@ int main(int, char **)
InitShaderOperations("shaders/"); // Initialize shader operations InitShaderOperations("shaders/"); // Initialize shader operations
if (!InitHistogramTFResources("shaders/")) { if (!InitHistogramResources("shaders/")) {
// Handle error - maybe disable histogram feature // Handle error - maybe disable histogram feature
fprintf(stderr, "Histogram initialization failed, feature disabled.\n"); fprintf(stderr, "Histogram initialization failed, feature disabled.\n");
} }
@ -1480,13 +1428,6 @@ int main(int, char **)
textureToSave = 0; textureToSave = 0;
} }
if (g_imageIsLoaded && textureToSave != 0) {
ComputeHistogramTF(textureToSave, g_loadedImage.getWidth(), g_loadedImage.getHeight()); // <<< Call TF version
} else {
std::fill(g_histogramCountsCPU.begin(), g_histogramCountsCPU.end(), 0);
g_histogramMaxCountTF = 1;
}
// --- Menu Bar --- // --- Menu Bar ---
if (ImGui::BeginMainMenuBar()) if (ImGui::BeginMainMenuBar())
{ {
@ -1799,6 +1740,7 @@ int main(int, char **)
GLuint displayTexId = textureToDisplay; // Use the display texture ID GLuint displayTexId = textureToDisplay; // Use the display texture ID
if (displayTexId != 0) if (displayTexId != 0)
{ {
ComputeHistogramGPU(displayTexId, g_loadedImage.getWidth(), g_loadedImage.getHeight());
// Assume ImGuiTexInspect fills available space. This might need adjustment. // Assume ImGuiTexInspect fills available space. This might need adjustment.
ImVec2 displaySize = availableContentSize; ImVec2 displaySize = availableContentSize;
float displayAspect = displaySize.x / displaySize.y; float displayAspect = displaySize.x / displaySize.y;
@ -1990,6 +1932,8 @@ int main(int, char **)
ImVec2 textSize = ImGui::CalcTextSize("No Image Loaded"); ImVec2 textSize = ImGui::CalcTextSize("No Image Loaded");
ImGui::SetCursorPos(ImVec2((winSize.x - textSize.x) * 0.5f, (winSize.y - textSize.y) * 0.5f)); ImGui::SetCursorPos(ImVec2((winSize.x - textSize.x) * 0.5f, (winSize.y - textSize.y) * 0.5f));
ImGui::Text("No Image Loaded. File -> Open... to load an image"); ImGui::Text("No Image Loaded. File -> Open... to load an image");
std::fill(g_histogramDataCPU.begin(), g_histogramDataCPU.end(), 0);
g_histogramMaxCount = 1;
// Or maybe: "File -> Open... to load an image" // Or maybe: "File -> Open... to load an image"
} }
ImGui::End(); // End Image View ImGui::End(); // End Image View
@ -1998,6 +1942,7 @@ int main(int, char **)
ImGui::Begin("Image Exif"); ImGui::Begin("Image Exif");
if (g_imageIsLoaded) if (g_imageIsLoaded)
{ {
ComputeHistogramGPU(textureToDisplay, g_loadedImage.getWidth(), g_loadedImage.getHeight());
ImGui::Text("Image Width: %d", g_loadedImage.m_width); ImGui::Text("Image Width: %d", g_loadedImage.m_width);
ImGui::Text("Image Height: %d", g_loadedImage.m_height); ImGui::Text("Image Height: %d", g_loadedImage.m_height);
ImGui::Text("Image Loaded: %s", g_imageIsLoaded ? "Yes" : "No"); ImGui::Text("Image Loaded: %s", g_imageIsLoaded ? "Yes" : "No");
@ -2315,9 +2260,9 @@ int main(int, char **)
g_loadedImage.m_textureId = 0; g_loadedImage.m_textureId = 0;
} }
if (g_histogramTFResourcesInitialized) { if (g_histogramResourcesInitialized) {
if (g_histogramTFVAO) glDeleteBuffers(1, &g_histogramTFVAO); if (g_histogramSSBO) glDeleteBuffers(1, &g_histogramSSBO);
if (g_histogramTFShader) glDeleteProgram(g_histogramTFShader); if (g_histogramComputeShader) glDeleteProgram(g_histogramComputeShader);
printf("Cleaned up histogram resources.\n"); printf("Cleaned up histogram resources.\n");
} }

View File

@ -15,19 +15,6 @@ layout(std430, binding = 1) buffer HistogramBuffer {
// Workgroup size (adjust based on GPU architecture for performance, 16x16 is often reasonable) // Workgroup size (adjust based on GPU architecture for performance, 16x16 is often reasonable)
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
// Helper to convert linear float (potentially HDR 0.0+) to 0-255 bin index
// Applies an approximate gamma curve for perceptual binning.
uint linearToVisualBin(float linearVal) {
// Clamp linear value to 0.0-1.0 range before gamma/binning
// This histograms the displayable range after processing.
float clampedVal = clamp(linearVal, 0.0, 1.0);
// Apply approximate sRGB gamma for perceptual brightness mapping
float displayApprox = pow(clampedVal, 1.0/2.2);
// Convert 0.0-1.0 display value to 0-255 bin index
return uint(displayApprox * 255.999); // Use 255.999 to ensure 1.0 maps to 255
}
void main() { void main() {
// Get the global invocation ID (like pixel coordinates) // Get the global invocation ID (like pixel coordinates)
ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy); ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
@ -43,9 +30,10 @@ void main() {
// Calculate bin indices (0-255) // Calculate bin indices (0-255)
// We clamp just in case, although imageLoad from rgba8 should be in range. // We clamp just in case, although imageLoad from rgba8 should be in range.
uint rBin = linearToVisualBin(pixelColor.r); uint rBin = uint(clamp(pixelColor.r, 0.0, 1.0) * 255.0);
uint gBin = linearToVisualBin(pixelColor.g); uint gBin = uint(clamp(pixelColor.g, 0.0, 1.0) * 255.0);
uint bBin = linearToVisualBin(pixelColor.b); uint bBin = uint(clamp(pixelColor.b, 0.0, 1.0) * 255.0);
// Atomically increment the counters in the SSBO // Atomically increment the counters in the SSBO
// Offset Green bins by 256, Blue bins by 512 // Offset Green bins by 256, Blue bins by 512
atomicAdd(histogram.bins[rBin], 1u); atomicAdd(histogram.bins[rBin], 1u);