initial commit
This commit is contained in:
parent
a8155a2d85
commit
79047a133f
20
imgui.ini
20
imgui.ini
@ -261,26 +261,6 @@ Pos=290,135
|
||||
Size=700,450
|
||||
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]
|
||||
DockSpace ID=0xE098E157 Window=0x9E772337 Pos=0,19 Size=3840,2072 Split=X
|
||||
DockNode ID=0x00000001 Parent=0xE098E157 SizeRef=1641,720 Split=X
|
||||
|
341
main.cpp
341
main.cpp
@ -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 int g_selectedAspectRatioIndex = 0; // Index for the dropdown
|
||||
|
||||
static GLuint g_histogramTFShader = 0; // Program with VS+GS
|
||||
static GLuint g_histogramTFBuffer = 0; // Buffer to store bin indices from TF
|
||||
static GLuint g_histogramTFQuery = 0; // Query object to count primitives written
|
||||
static GLuint g_histogramTFVAO = 0; // Dummy VAO for drawing points
|
||||
static size_t g_histogramTFBufferSize = 0; // Size in bytes
|
||||
static std::vector<float> g_histogramTFDataCPU; // CPU buffer for readback (float)
|
||||
// Keep the final histogram count data
|
||||
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;
|
||||
static GLuint g_histogramComputeShader = 0;
|
||||
static GLuint g_histogramSSBO = 0;
|
||||
const int NUM_HISTOGRAM_BINS = 256;
|
||||
const int HISTOGRAM_BUFFER_SIZE = NUM_HISTOGRAM_BINS * 3; // R, G, B
|
||||
static std::vector<unsigned int> g_histogramDataCPU(HISTOGRAM_BUFFER_SIZE, 0);
|
||||
static unsigned int g_histogramMaxCount = 255; // Max count found, for scaling (init to 1 to avoid div by zero)
|
||||
static bool g_histogramResourcesInitialized = false;
|
||||
|
||||
// Interaction state
|
||||
enum class CropHandle
|
||||
@ -672,91 +667,75 @@ static bool g_isDraggingCrop = false;
|
||||
static ImVec2 g_dragStartMousePos = ImVec2(0, 0); // Screen coords
|
||||
|
||||
|
||||
bool InitHistogramTFResources(const std::string& shaderBasePath) {
|
||||
printf("Initializing Histogram Transform Feedback Resources...\n");
|
||||
g_histogramTFResourcesInitialized = false; // Assume failure until success
|
||||
|
||||
// 1. Load Vertex and Geometry Shaders
|
||||
std::string vsSource = ReadFile(shaderBasePath + "histogram_tf.vert");
|
||||
std::string gsSource = ReadFile(shaderBasePath + "histogram_tf.geom");
|
||||
if (vsSource.empty() || gsSource.empty()) {
|
||||
fprintf(stderr, "ERROR: Failed to read histogram_tf shaders.\n");
|
||||
bool InitHistogramResources(const std::string& shaderBasePath) {
|
||||
printf("Initializing Histogram Resources...\n");
|
||||
// Load Compute Shader
|
||||
// We need a way to load compute shaders, modify shader_utils or add here
|
||||
std::string compSource = ReadFile(shaderBasePath + "histogram.comp"); // Assuming ReadFile exists
|
||||
if (compSource.empty()) {
|
||||
fprintf(stderr, "ERROR: Failed to read histogram.comp\n");
|
||||
return false;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource); // Assuming CompileShader exists
|
||||
GLuint gs = CompileShader(GL_GEOMETRY_SHADER, gsSource);
|
||||
if (!vs || !gs) {
|
||||
if(vs) glDeleteShader(vs);
|
||||
if(gs) glDeleteShader(gs);
|
||||
|
||||
g_histogramComputeShader = glCreateProgram();
|
||||
glAttachShader(g_histogramComputeShader, computeShaderObj);
|
||||
glLinkProgram(g_histogramComputeShader);
|
||||
// --- 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;
|
||||
}
|
||||
|
||||
// 2. Create and Link Shader Program
|
||||
g_histogramTFShader = glCreateProgram();
|
||||
glAttachShader(g_histogramTFShader, vs);
|
||||
glAttachShader(g_histogramTFShader, gs);
|
||||
|
||||
// 3. Specify Transform Feedback Varying *** BEFORE LINKING ***
|
||||
const char* varyings[] = { "tf_BinIndex" }; // Must match 'out' variable in GS
|
||||
glTransformFeedbackVaryings(g_histogramTFShader, 1, varyings, GL_INTERLEAVED_ATTRIBS); // Or GL_SEPARATE_ATTRIBS if needed
|
||||
glDeleteShader(computeShaderObj); // Delete shader object after linking
|
||||
printf("Histogram compute shader loaded and linked successfully (Program ID: %u).\n", g_histogramComputeShader);
|
||||
|
||||
glLinkProgram(g_histogramTFShader);
|
||||
|
||||
// --- Add linking error checks (glGetProgramiv, glGetProgramInfoLog) ---
|
||||
GLint linkSuccess;
|
||||
glGetProgramiv(g_histogramTFShader, GL_LINK_STATUS, &linkSuccess);
|
||||
if (!linkSuccess) {
|
||||
// ... get and print link error log ...
|
||||
fprintf(stderr, "ERROR: Failed to link histogram_tf shader program.\n");
|
||||
glDeleteProgram(g_histogramTFShader); g_histogramTFShader = 0;
|
||||
glDeleteShader(vs); glDeleteShader(gs);
|
||||
// Create Shader Storage Buffer Object (SSBO)
|
||||
glGenBuffers(1, &g_histogramSSBO);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, g_histogramSSBO);
|
||||
// Allocate buffer size: 3 channels * 256 bins * size of uint
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int), NULL, GL_DYNAMIC_READ); // Data will be written by GPU, read by CPU
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind
|
||||
|
||||
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;
|
||||
} 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
|
||||
// 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;
|
||||
g_histogramResourcesInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1095,127 +1074,96 @@ void InitShaderOperations(const std::string &shaderBasePath)
|
||||
|
||||
// Add this function somewhere accessible, e.g., before main()
|
||||
|
||||
void ComputeHistogramTF(GLuint linearTextureID, int width, int height) {
|
||||
if (!g_histogramTFResourcesInitialized || linearTextureID == 0 || width <= 0 || height <= 0) {
|
||||
std::fill(g_histogramCountsCPU.begin(), g_histogramCountsCPU.end(), 0);
|
||||
g_histogramMaxCountTF = 1;
|
||||
void ComputeHistogramGPU(GLuint inputTextureID, int width, int height) {
|
||||
if (!g_histogramResourcesInitialized || inputTextureID == 0 || width <= 0 || height <= 0) {
|
||||
// Clear CPU data if not computed
|
||||
std::fill(g_histogramDataCPU.begin(), g_histogramDataCPU.end(), 0);
|
||||
g_histogramMaxCount = 1;
|
||||
printf("Histogram resources not initialized or invalid input. Skipping computation.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t requiredFloats = static_cast<size_t>(width) * height * 3;
|
||||
size_t requiredBytes = requiredFloats * sizeof(float);
|
||||
|
||||
// 1. Ensure TF Buffer is large enough
|
||||
if (requiredBytes > g_histogramTFBufferSize) {
|
||||
printf("Resizing Histogram TF buffer to %zu bytes\n", requiredBytes);
|
||||
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, g_histogramTFBuffer);
|
||||
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, requiredBytes, NULL, GL_DYNAMIC_READ); // Resize/allocate
|
||||
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
|
||||
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);
|
||||
}
|
||||
}
|
||||
// 1. Clear the SSBO buffer data to zeros
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, g_histogramSSBO);
|
||||
// Using glBufferSubData might be marginally faster than glClearBufferData if driver optimizes zeroing
|
||||
// static std::vector<unsigned int> zeros(HISTOGRAM_BUFFER_SIZE, 0); // Create once
|
||||
// glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int), zeros.data());
|
||||
// Or use glClearBufferData (often recommended)
|
||||
GLuint zero = 0;
|
||||
glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &zero);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind
|
||||
|
||||
|
||||
// 2. Setup Transform Feedback State
|
||||
glUseProgram(g_histogramTFShader);
|
||||
// 2. Bind resources and dispatch compute shader
|
||||
glUseProgram(g_histogramComputeShader);
|
||||
|
||||
// Bind the input texture (Linear Float format)
|
||||
glActiveTexture(GL_TEXTURE0); // Use texture unit 0
|
||||
glBindTexture(GL_TEXTURE_2D, linearTextureID);
|
||||
glUniform1i(glGetUniformLocation(g_histogramTFShader, "InputTexture"), 0); // Tell shader sampler is on unit 0
|
||||
// Bind input texture as image unit 0 (read-only)
|
||||
// IMPORTANT: Ensure the format matches the compute shader layout qualifier (e.g., rgba8)
|
||||
// If textureToDisplay is RGBA16F, you'd use layout(rgba16f) in shader
|
||||
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
|
||||
glEnable(GL_RASTERIZER_DISCARD);
|
||||
// Bind SSBO to binding point 1
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, g_histogramSSBO);
|
||||
|
||||
// Bind the buffer for transform feedback output
|
||||
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, g_histogramTFBuffer); // Binding index 0
|
||||
// Calculate number of work groups
|
||||
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
|
||||
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, g_histogramTFQuery);
|
||||
glBeginTransformFeedback(GL_POINTS); // Outputting points from GS
|
||||
// Dispatch the compute shader
|
||||
glDispatchCompute(numGroupsX, numGroupsY, 1);
|
||||
|
||||
// 4. Draw Points (one per pixel) - using dummy VAO
|
||||
glBindVertexArray(g_histogramTFVAO);
|
||||
glDrawArrays(GL_POINTS, 0, width * height); // Draw one point for each pixel
|
||||
glBindVertexArray(0);
|
||||
// 3. Synchronization: Ensure compute shader writes finish before CPU reads buffer
|
||||
// Use a memory barrier on the SSBO writes
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
|
||||
// 5. End Transform Feedback and Query
|
||||
glEndTransformFeedback();
|
||||
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
|
||||
|
||||
// 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);
|
||||
// Unbind resources (optional here, but good practice)
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0);
|
||||
glBindImageTexture(0, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA8);
|
||||
glUseProgram(0);
|
||||
|
||||
// Optional: Get number of primitives written from query
|
||||
// GLuint primitivesWritten = 0;
|
||||
// glGetQueryObjectuiv(g_histogramTFQuery, GL_QUERY_RESULT, &primitivesWritten);
|
||||
// printf("TF Primitives Written: %u (Expected: %d)\n", primitivesWritten, width * height * 3);
|
||||
// 4. Read histogram data back from SSBO to CPU vector
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, g_histogramSSBO);
|
||||
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, HISTOGRAM_BUFFER_SIZE * sizeof(unsigned int), g_histogramDataCPU.data());
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind
|
||||
|
||||
// 6. Read back the Transform Feedback buffer
|
||||
// Make sure TF operations are finished. glFlush/glFinish might be needed
|
||||
// depending on driver, but often implicitly synced by readback. Add if needed.
|
||||
// glFlush();
|
||||
glBindBuffer(GL_ARRAY_BUFFER, g_histogramTFBuffer); // Bind to generic target for readback
|
||||
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];
|
||||
}
|
||||
// 5. Find the maximum count for scaling the plot (optional, can be capped)
|
||||
g_histogramMaxCount = 255; // Reset to 255 (prevents div by zero)
|
||||
for (unsigned int count : g_histogramDataCPU) {
|
||||
if (count > g_histogramMaxCount) {
|
||||
g_histogramMaxCount = count;
|
||||
}
|
||||
// else { printf("Warning: Out of bounds TF bin index: %d\n", binIndex); } // Debugging
|
||||
}
|
||||
// if (g_histogramMaxCountTF == 1) {
|
||||
// printf("Warning: Max histogram count is still 1 after TF processing.\n");
|
||||
// }
|
||||
// Optional: Cap max count to prevent extreme peaks from flattening the rest
|
||||
// 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()
|
||||
|
||||
void DrawHistogramWidget(const char* widgetId, ImVec2 graphSize) {
|
||||
if (g_histogramCountsCPU.empty() || g_histogramMaxCountTF <= 1) { // Check if data is valid
|
||||
if (g_histogramCountsCPU.empty()) {
|
||||
if (g_histogramDataCPU.empty() || g_histogramMaxCount <= 1) { // Check if data is valid
|
||||
if (g_histogramDataCPU.empty()) {
|
||||
ImGui::Text("Histogram data not initialized.");
|
||||
} else {
|
||||
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 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));
|
||||
|
||||
// Calculate scaling factors
|
||||
float barWidth = graphSize.x / float(NUM_HISTOGRAM_BINS_TF);
|
||||
float scaleY = graphSize.y / float(g_histogramMaxCountTF);
|
||||
float barWidth = graphSize.x / float(NUM_HISTOGRAM_BINS);
|
||||
float scaleY = graphSize.y / float(g_histogramMaxCount); // Scale based on max count
|
||||
|
||||
// Define colors (with some transparency for overlap visibility)
|
||||
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);
|
||||
|
||||
// Draw the histogram bars (R, G, B)
|
||||
for (int i = 0; i < NUM_HISTOGRAM_BINS_TF; ++i) {
|
||||
// Get heights from g_histogramCountsCPU
|
||||
float hR = ImMin(float(g_histogramCountsCPU[i]) * scaleY, graphSize.y);
|
||||
float hG = ImMin(float(g_histogramCountsCPU[i + NUM_HISTOGRAM_BINS_TF]) * scaleY, graphSize.y);
|
||||
float hB = ImMin(float(g_histogramCountsCPU[i + NUM_HISTOGRAM_BINS_TF * 2]) * scaleY, graphSize.y);
|
||||
for (int i = 0; i < NUM_HISTOGRAM_BINS; ++i) {
|
||||
// Get heights (clamped to graph size)
|
||||
float hR = ImMin(float(g_histogramDataCPU[i]) * scaleY, graphSize.y);
|
||||
float hG = ImMin(float(g_histogramDataCPU[i + NUM_HISTOGRAM_BINS]) * scaleY, graphSize.y);
|
||||
float hB = ImMin(float(g_histogramDataCPU[i + NUM_HISTOGRAM_BINS * 2]) * scaleY, graphSize.y);
|
||||
|
||||
// Calculate bar positions
|
||||
float x0 = widgetPos.x + float(i) * barWidth;
|
||||
@ -1410,7 +1358,7 @@ int main(int, char **)
|
||||
|
||||
InitShaderOperations("shaders/"); // Initialize shader operations
|
||||
|
||||
if (!InitHistogramTFResources("shaders/")) {
|
||||
if (!InitHistogramResources("shaders/")) {
|
||||
// Handle error - maybe disable histogram feature
|
||||
fprintf(stderr, "Histogram initialization failed, feature disabled.\n");
|
||||
}
|
||||
@ -1480,13 +1428,6 @@ int main(int, char **)
|
||||
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 ---
|
||||
if (ImGui::BeginMainMenuBar())
|
||||
{
|
||||
@ -1799,6 +1740,7 @@ int main(int, char **)
|
||||
GLuint displayTexId = textureToDisplay; // Use the display texture ID
|
||||
if (displayTexId != 0)
|
||||
{
|
||||
ComputeHistogramGPU(displayTexId, g_loadedImage.getWidth(), g_loadedImage.getHeight());
|
||||
// Assume ImGuiTexInspect fills available space. This might need adjustment.
|
||||
ImVec2 displaySize = availableContentSize;
|
||||
float displayAspect = displaySize.x / displaySize.y;
|
||||
@ -1990,6 +1932,8 @@ int main(int, char **)
|
||||
ImVec2 textSize = ImGui::CalcTextSize("No Image Loaded");
|
||||
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");
|
||||
std::fill(g_histogramDataCPU.begin(), g_histogramDataCPU.end(), 0);
|
||||
g_histogramMaxCount = 1;
|
||||
// Or maybe: "File -> Open... to load an image"
|
||||
}
|
||||
ImGui::End(); // End Image View
|
||||
@ -1998,6 +1942,7 @@ int main(int, char **)
|
||||
ImGui::Begin("Image Exif");
|
||||
if (g_imageIsLoaded)
|
||||
{
|
||||
ComputeHistogramGPU(textureToDisplay, g_loadedImage.getWidth(), g_loadedImage.getHeight());
|
||||
ImGui::Text("Image Width: %d", g_loadedImage.m_width);
|
||||
ImGui::Text("Image Height: %d", g_loadedImage.m_height);
|
||||
ImGui::Text("Image Loaded: %s", g_imageIsLoaded ? "Yes" : "No");
|
||||
@ -2315,9 +2260,9 @@ int main(int, char **)
|
||||
g_loadedImage.m_textureId = 0;
|
||||
}
|
||||
|
||||
if (g_histogramTFResourcesInitialized) {
|
||||
if (g_histogramTFVAO) glDeleteBuffers(1, &g_histogramTFVAO);
|
||||
if (g_histogramTFShader) glDeleteProgram(g_histogramTFShader);
|
||||
if (g_histogramResourcesInitialized) {
|
||||
if (g_histogramSSBO) glDeleteBuffers(1, &g_histogramSSBO);
|
||||
if (g_histogramComputeShader) glDeleteProgram(g_histogramComputeShader);
|
||||
printf("Cleaned up histogram resources.\n");
|
||||
}
|
||||
|
||||
|
@ -15,19 +15,6 @@ layout(std430, binding = 1) buffer HistogramBuffer {
|
||||
// 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;
|
||||
|
||||
|
||||
// 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() {
|
||||
// Get the global invocation ID (like pixel coordinates)
|
||||
ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
|
||||
@ -43,9 +30,10 @@ void main() {
|
||||
|
||||
// Calculate bin indices (0-255)
|
||||
// We clamp just in case, although imageLoad from rgba8 should be in range.
|
||||
uint rBin = linearToVisualBin(pixelColor.r);
|
||||
uint gBin = linearToVisualBin(pixelColor.g);
|
||||
uint bBin = linearToVisualBin(pixelColor.b);
|
||||
uint rBin = uint(clamp(pixelColor.r, 0.0, 1.0) * 255.0);
|
||||
uint gBin = uint(clamp(pixelColor.g, 0.0, 1.0) * 255.0);
|
||||
uint bBin = uint(clamp(pixelColor.b, 0.0, 1.0) * 255.0);
|
||||
|
||||
// Atomically increment the counters in the SSBO
|
||||
// Offset Green bins by 256, Blue bins by 512
|
||||
atomicAdd(histogram.bins[rBin], 1u);
|
||||
|
Loading…
x
Reference in New Issue
Block a user