Revision: 76974
http://sourceforge.net/p/brlcad/code/76974
Author: starseeker
Date: 2020-08-27 12:17:55 +0000 (Thu, 27 Aug 2020)
Log Message:
-----------
Experiment is in swrast branch.
Modified Paths:
--------------
brlcad/trunk/src/libdm/tests/CMakeLists.txt
Removed Paths:
-------------
brlcad/trunk/src/libdm/tests/rendered_image.cpp
Modified: brlcad/trunk/src/libdm/tests/CMakeLists.txt
===================================================================
--- brlcad/trunk/src/libdm/tests/CMakeLists.txt 2020-08-27 12:04:42 UTC (rev
76973)
+++ brlcad/trunk/src/libdm/tests/CMakeLists.txt 2020-08-27 12:17:55 UTC (rev
76974)
@@ -39,38 +39,16 @@
if (BRLCAD_ENABLE_TK)
include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR}
${TCL_INCLUDE_PATH}
${TK_INCLUDE_PATH}
)
-if (0)
- set(renderer_srcs
- renderer/camera.c
- renderer/darray.c
- renderer/draw2d.c
- renderer/graphics.c
- renderer/image.c
- renderer/maths.c
- renderer/mesh.c
- renderer/private.c
- renderer/scene.c
- renderer/skeleton.c
- renderer/texture.c
- renderer/scenes/blinn_shader.c
- renderer/scenes/cache_helper.c
- renderer/scenes/scene_helper.c
- )
- BRLCAD_ADDEXEC(rendered_img "rendered_image.cpp;${renderer_srcs}"
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
-endif (0)
-
- BRLCAD_ADDEXEC(tcl_img tcl_img.cpp "libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}"
TEST_USEDATA)
- BRLCAD_ADDEXEC(tcl_tevent tcl_tevent.cpp
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
+ BRLCAD_ADDEXEC(tcl_img tcl_img.cpp
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
+ BRLCAD_ADDEXEC(tcl_tevent tcl_tevent.cpp
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
endif (BRLCAD_ENABLE_TK)
CMAKEFILES(CMakeLists.txt)
CMAKEFILES(
- rendered_image.cpp
tcl_img.cpp
tcl_tevent.cpp
)
Deleted: brlcad/trunk/src/libdm/tests/rendered_image.cpp
===================================================================
--- brlcad/trunk/src/libdm/tests/rendered_image.cpp 2020-08-27 12:04:42 UTC
(rev 76973)
+++ brlcad/trunk/src/libdm/tests/rendered_image.cpp 2020-08-27 12:17:55 UTC
(rev 76974)
@@ -1,974 +0,0 @@
-/* R E N D E R E D _ I M G . C P P
- * BRL-CAD
- *
- * Published in 2020 by the United States Government.
- * This work is in the public domain.
- *
- * Portions from zauonlok/renderer by Zhou Le are MIT Licensed.
- * If this is becomes anything more than a throwaway test/demo will need to
- * separate and structure the renderer code properly.
- *
- */
-/** @file rendered_img.cpp
- *
- * Working with image data in Tcl/Tk in C/C++
- *
- */
-
-#include "common.h"
-
-#include <climits>
-#include <random>
-#include <iostream>
-#include <string>
-#include <stdlib.h>
-#include <errno.h>
-
-#include "bu/app.h"
-#include "bu/malloc.h"
-
-#include "tcl.h"
-#include "tk.h"
-
-#include "renderer/api.h"
-
-static const vec3_t CAMERA_POSITION = {0, 0, 1.5f};
-static const vec3_t CAMERA_TARGET = {0, 0, 0};
-
-typedef struct {
- framebuffer_t *framebuffer;
- camera_t *camera;
- vec3_t light_dir;
- vec2_t click_pos;
- int single_click;
- int double_click;
- float frame_time;
- float delta_time;
-} context_t;
-
-static mat4_t get_light_view_matrix(vec3_t light_dir) {
- vec3_t light_pos = vec3_negate(light_dir);
- vec3_t light_target = vec3_new(0, 0, 0);
- vec3_t light_up = vec3_new(0, 1, 0);
- return mat4_lookat(light_pos, light_target, light_up);
-}
-
-static mat4_t get_light_proj_matrix(float half_w, float half_h,
- float z_near, float z_far) {
- return mat4_orthographic(half_w, half_h, z_near, z_far);
-}
-
-static int compare_models(const void *model1p, const void *model2p) {
- model_t *model1 = *(model_t**)model1p;
- model_t *model2 = *(model_t**)model2p;
-
- if (model1->opaque && model2->opaque) {
- return model1->distance < model2->distance ? -1 : 1;
- } else if (model1->opaque && !model2->opaque) {
- return -1;
- } else if (!model1->opaque && model2->opaque) {
- return 1;
- } else {
- return model1->distance < model2->distance ? 1 : -1;
- }
-}
-
-static void sort_models(model_t **models, mat4_t view_matrix) {
- int num_models = darray_size(models);
- int i;
- if (num_models > 1) {
- for (i = 0; i < num_models; i++) {
- model_t *model = models[i];
- vec3_t center = mesh_get_center(model->mesh);
- vec4_t local_pos = vec4_from_vec3(center, 1);
- vec4_t world_pos = mat4_mul_vec4(model->transform, local_pos);
- vec4_t view_pos = mat4_mul_vec4(view_matrix, world_pos);
- model->distance = -view_pos.z;
- }
- qsort(models, num_models, sizeof(model_t*), compare_models);
- }
-}
-
-scene_t *blinn_lighthouse_scene(void) {
- mat4_t translation = mat4_translate(-78.203f, -222.929f, 16.181f);
- mat4_t rotation = mat4_rotate_y(TO_RADIANS(-135));
- mat4_t scale = mat4_scale(0.0016f, 0.0016f, 0.0016f);
- mat4_t root = mat4_mul_mat4(scale, mat4_mul_mat4(rotation, translation));
- return scene_from_file("lighthouse/lighthouse.scn", root);
-}
-
-perframe_t test_build_perframe(scene_t *scene, context_t *context) {
- vec3_t light_dir = vec3_normalize(context->light_dir);
- camera_t *camera = context->camera;
- perframe_t perframe;
-
- perframe.frame_time = context->frame_time;
- perframe.delta_time = context->delta_time;
- perframe.light_dir = light_dir;
- perframe.camera_pos = camera_get_position(camera);
- perframe.light_view_matrix = get_light_view_matrix(light_dir);
- perframe.light_proj_matrix = get_light_proj_matrix(1, 1, 0, 2);
- perframe.camera_view_matrix = camera_get_view_matrix(camera);
- perframe.camera_proj_matrix = camera_get_proj_matrix(camera);
- perframe.ambient_intensity = scene->ambient_intensity;
- perframe.punctual_intensity = scene->punctual_intensity;
- perframe.shadow_map = scene->shadow_map;
- perframe.layer_view = -1;
-
- return perframe;
-}
-
-void test_draw_scene(scene_t *scene, framebuffer_t *framebuffer, perframe_t
*perframe) {
- model_t **models = scene->models;
- int num_models = darray_size(models);
- int i;
-
- for (i = 0; i < num_models; i++) {
- model_t *model = models[i];
- model->update(model, perframe);
- }
-
- if (scene->shadow_buffer && scene->shadow_map) {
- sort_models(models, perframe->light_view_matrix);
- framebuffer_clear_depth(scene->shadow_buffer, 1);
- for (i = 0; i < num_models; i++) {
- model_t *model = models[i];
- if (model->opaque) {
- model->draw(model, scene->shadow_buffer, 1);
- }
- }
- texture_from_depthbuffer(scene->shadow_map, scene->shadow_buffer);
- }
-
- sort_models(models, perframe->camera_view_matrix);
- framebuffer_clear_color(framebuffer, scene->background);
- framebuffer_clear_depth(framebuffer, 1);
- for (i = 0; i < num_models; i++) {
- model_t *model = models[i];
- model->draw(model, framebuffer, 0);
- }
-}
-
-
-const char *DM_PHOTO = ".dm0.photo";
-const char *DM_CANVAS = ".dm0";
-
-TCL_DECLARE_MUTEX(dilock)
-TCL_DECLARE_MUTEX(fblock)
-TCL_DECLARE_MUTEX(threadMutex)
-
-/* Container holding image generation information - need to be able
- * to pass these to the update command */
-struct img_data {
- // These flags should be mutex guarded.
- int dm_render_needed;
- int dm_render_running;
- int dm_render_ready;
-
- int fb_render_running;
- int fb_render_ready;
-
- // Parent application sets this when it's time to shut
- // down all rendering
- int shutdown;
-
- // Main thread id - used to send a rendering event back to
- // the parent thread.
- Tcl_ThreadId parent_id;
-
- // Tcl interp can only be used by the thread that created it, and the
- // Tk_Photo calls require an interpreter. Thus we keep track of which
- // interpreter is the parent interp in the primary data container, as this
- // interp (and ONLY this interp) is requried to do Tk_Photo processing when
- // triggered from thread events sent back to the parent. Those callbacks
- // don't by default encode knowledge about the parent's Tcl_Interp, so we
- // store it where we know we can access it.
- Tcl_Interp *parent_interp;
-
- // DM thread id
- Tcl_ThreadId dm_id;
-
- // FB thread_id
- Tcl_ThreadId fb_id;
-
- // Image info. Reflects the currently requested parent image
- // size - the render thread may have a different size during
- // rendering, as it "snapshots" these numbers to have a stable
- // buffer size when actually writing pixels.
- int dm_width;
- int dm_height;
-
- // Actual allocated sizes of image buffer
- int dm_bwidth;
- int dm_bheight;
-
- // Used for image generation - these are presisted across renders so the
- // image contents will vary as expected.
- std::default_random_engine *gen;
- std::uniform_int_distribution<int> *colors;
- std::uniform_int_distribution<int> *vals;
-
- // The rendering memory used to actually generate the DM scene contents.
- long dm_buff_size;
- unsigned char *dmpixel;
-
- // The rendering memory used to actually generate the framebuffer contents.
- long fb_buff_size;
- int fb_width;
- int fb_height;
- unsigned char *fbpixel;
-};
-
-// Event container passed to routines triggered by events.
-struct RenderEvent {
- Tcl_Event event; /* Must be first */
- struct img_data *idata;
-};
-
-// Event container passed to routines triggered by events.
-struct FbRenderEvent {
- Tcl_Event event; /* Must be first */
- struct img_data *idata;
-};
-
-// Even for events where we don't intend to actually run a proc,
-// we need to tell Tcl it successfully processed them. For that
-// we define a no-op callback proc.
-static int
-noop_proc(Tcl_Event *UNUSED(evPtr), int UNUSED(mask))
-{
- // Return one to signify a successful completion of the process execution
- return 1;
-}
-
-// The actual Tk_Photo updating must be done by the creater of the
-// Tcl_Interp - which is the parent thread. The interp below must
-// thus be the PARENT's interp, and we have passed it through the
-// various structures to be available here.
-//
-// TODO - see if we can use the Tk_PhotoPutZoomedBlock API to keep the image in
-// sync with the window size as we're moving; even if it's not a fully crisp
-// rendering, then catch up once a properly sized rendering is done...
-static int
-UpdateProc(Tcl_Event *evPtr, int UNUSED(mask))
-{
- struct RenderEvent *DmEventPtr = (RenderEvent *) evPtr;
- struct img_data *idata = DmEventPtr->idata;
- Tcl_Interp *interp = idata->parent_interp;
-
- Tcl_MutexLock(&dilock);
- Tcl_MutexLock(&fblock);
-
- // Render processing now - reset the ready flag
- Tcl_MutexLock(&threadMutex);
- idata->dm_render_ready = 0;
- Tcl_MutexUnlock(&threadMutex);
-
- int width = idata->dm_bwidth;
- int height = idata->dm_bheight;
-
- // If we're mid-render, don't update - that means another render
- // got triggered after this proc got started, and another update
- // event will eventually be triggered.
- if (idata->dm_render_running) {
- Tcl_MutexUnlock(&dilock);
- Tcl_MutexUnlock(&fblock);
- return 1;
- }
-
- std::cout << "Window updating!\n";
-
- // Let Tcl/Tk know the photo data has changed, so it can update the visuals
- // accordingly
- Tk_PhotoImageBlock dm_data;
- Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
- Tk_PhotoGetImage(dm_img, &dm_data);
- if (dm_data.width != width || dm_data.height != height) {
- Tk_PhotoSetSize(interp, dm_img, width, height);
- Tk_PhotoGetImage(dm_img, &dm_data);
- }
-
-
- // Tk_PhotoPutBlock appears to be making a copy of the data, so we should
- // be able to point to our thread's rendered data to feed it in for
- // copying without worrying that Tk might alter it.
- dm_data.pixelPtr = idata->dmpixel;
- Tk_PhotoPutBlock(interp, dm_img, &dm_data, 0, 0, dm_data.width,
dm_data.height, TK_PHOTO_COMPOSITE_SET);
-
-
- // Now overlay the framebuffer.
- Tk_PhotoImageBlock fb_data;
- Tk_PhotoGetImage(dm_img, &fb_data);
- fb_data.width = idata->fb_width;
- fb_data.height = idata->fb_height;
- fb_data.pitch = fb_data.width * 4;
- fb_data.pixelPtr = idata->fbpixel;
- Tk_PhotoPutBlock(interp, dm_img, &fb_data, 0, 0, idata->fb_width,
idata->fb_height, TK_PHOTO_COMPOSITE_OVERLAY);
-
- Tcl_MutexUnlock(&dilock);
- Tcl_MutexUnlock(&fblock);
-
- // Return one to signify a successful completion of the process execution
- return 1;
-}
-
-int
-image_update_data(ClientData clientData, Tcl_Interp *interp, int argc, char
**argv)
-{
- struct img_data *idata = (struct img_data *)clientData;
-
- if (argc == 3) {
- // Unpack the current width and height. If these don't match what
idata has
- // or thinks its working on, update the image size.
- // (Note: checking errno, although it may not truly be necessary if we
- // trust Tk to always give us valid coordinates...)
- char *p_end;
- errno = 0;
- long width = strtol(argv[1], &p_end, 10);
- if (errno == ERANGE || (errno != 0 && width == 0) || p_end == argv[1]) {
- std::cerr << "Invalid width: " << argv[1] << "\n";
- return TCL_ERROR;
- }
- errno = 0;
- long height = strtol(argv[2], &p_end, 10);
- if (errno == ERANGE || (errno != 0 && height == 0) || p_end == argv[1])
{
- std::cerr << "Invalid height: " << argv[2] << "\n";
- return TCL_ERROR;
- }
-
- Tcl_MutexLock(&dilock);
- idata->dm_width = width;
- idata->dm_height = height;
- Tcl_MutexUnlock(&dilock);
-
- Tk_PhotoImageBlock dm_data;
- Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
- Tk_PhotoGetImage(dm_img, &dm_data);
- if (dm_data.width != idata->dm_width || dm_data.height !=
idata->dm_height) {
- Tk_PhotoSetSize(interp, dm_img, idata->dm_width, idata->dm_height);
- }
- }
-
- // Generate an event for the manager thread to let it know we need work
- Tcl_MutexLock(&threadMutex);
- struct RenderEvent *threadEventPtr = (struct RenderEvent
*)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = noop_proc;
- Tcl_ThreadQueueEvent(idata->dm_id, (Tcl_Event *) threadEventPtr,
TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->dm_id);
- Tcl_MutexUnlock(&threadMutex);
-
- return TCL_OK;
-}
-
-// Given an X,Y coordinate in a TkPhoto image and a desired offset in
-// X and Y, return the index value into the pixelPtr array N such that
-// N is the integer value of the R color at that X,Y coordiante, N+1
-// is the G value, N+2 is the B value and N+3 is the Alpha value.
-// If either desired offset is beyond the width and height boundaries,
-// cap the return at the minimum/maximum allowed value.
-static int
-img_xy_index(int width, int height, int x, int y, int dx, int dy)
-{
- int nx = ((x + dx) > width) ? width : ((x + dx) < 0) ? 0 : x + dx;
- int ny = ((y + dy) > height) ? height : ((y + dy) < 0) ? 0 : y + dy;
- return (ny * width * 4) + nx * 4;
-}
-
-/* This is a test of directly manipulating the Tk_Photo data itself, without
- * copying in a buffer. Probably not something we want to do for real... */
-int
-image_paint_xy(ClientData clientData, Tcl_Interp *interp, int argc, char
**argv)
-{
- if (argc != 3) {
- std::cerr << "Unexpected argc: " << argc << "\n";
- return TCL_ERROR;
- }
-
- struct img_data *idata = (struct img_data *)clientData;
-
- // If we're mid-render, don't draw - we're about to get superceded by
- // another frame.
- if (idata->dm_render_running) {
- return TCL_OK;
- }
-
- // Unpack the coordinates (checking errno, although it may not truly be
- // necessary if we trust Tk to always give us valid coordinates...)
- char *p_end;
- errno = 0;
- long xcoor = strtol(argv[1], &p_end, 10);
- if (errno == ERANGE || (errno != 0 && xcoor == 0) || p_end == argv[1]) {
- std::cerr << "Invalid image_paint_xy X coordinate: " << argv[1] << "\n";
- return TCL_ERROR;
- }
- errno = 0;
- long ycoor = strtol(argv[2], &p_end, 10);
- if (errno == ERANGE || (errno != 0 && ycoor == 0) || p_end == argv[1]) {
- std::cerr << "Invalid image_paint_xy Y coordinate: " << argv[2] << "\n";
- return TCL_ERROR;
- }
-
- // Look up the internals of the image - we're going to directly manipulate
- // the values of the image to simulate a display manager or framebuffer
- // changing the visual via rendering.
- Tk_PhotoImageBlock dm_data;
- Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
- Tk_PhotoGetImage(dm_img, &dm_data);
-
-
- for (int i = -2; i < 3; i++) {
- for (int j = -2; j < 3; j++) {
- int pindex = img_xy_index(dm_data.width, dm_data.height, xcoor,
ycoor, i, j);
- if (pindex >= idata->dm_buff_size - 1) {
- continue;
- }
- // Set to opaque white
- dm_data.pixelPtr[pindex] = 255;
- dm_data.pixelPtr[pindex+1] = 255;
- dm_data.pixelPtr[pindex+2] = 255;
- dm_data.pixelPtr[pindex+3] = 255;
- }
- }
-
- // Let Tcl/Tk know the photo data has changed, so it can update the
visuals accordingly.
- Tk_PhotoPutBlock(interp, dm_img, &dm_data, 0, 0, dm_data.width,
dm_data.height, TK_PHOTO_COMPOSITE_SET);
-
- return TCL_OK;
-}
-
-static Tcl_ThreadCreateType
-Dm_Render(ClientData clientData)
-{
- // Unpack the shared data object
- struct img_data *idata = (struct img_data *)clientData;
-
- // Lock updating of the idata values until we've gotten this run's key
- // information - we don't want this changing mid-render.
- Tcl_MutexLock(&dilock);
-
- // Anything before this point will be convered by this render. (This
- // flag may get set after this render starts by additional events, but
- // to this point this rendering will handle it.)
- idata->dm_render_needed = 0;
-
- // We're now in process - don't start another frame until this one
- // is complete.
- idata->dm_render_running = 1;
-
- // Until we're done, make sure nobody thinks we are ready
- idata->dm_render_ready = 0;
-
- // Lock in this width and height - that's what we've got memory for,
- // so that's what the remainder of this rendering pass will use.
- idata->dm_bwidth = idata->dm_width;
- idata->dm_bheight = idata->dm_height;
-
- // If we have insufficient memory, allocate or reallocate a local
- // buffer big enough for the purpose. We use our own buffer for
- // rendering to allow Tk full control over what it wants to do behind
- // the scenes. We need this memory to persist, so handle it from the
- // management thread.
- long b_minsize = idata->dm_bwidth * idata->dm_bheight * 4;
- if (!idata->dmpixel || idata->dm_buff_size < b_minsize) {
- idata->dmpixel = (unsigned char *)bu_realloc(idata->dmpixel,
2*b_minsize*sizeof(char), "realloc pixbuf");
- idata->dm_buff_size = b_minsize * 2;
- }
-
- // Have the key values now, we can unlock and allow interactivity in
- // the parent. We'll finish this rendering pass before paying any
- // further attention to parent settings, but the parent may now set
- // them for future consideration.
- Tcl_MutexUnlock(&dilock);
-
-
//////////////////////////////////////////////////////////////////////////////
- // Rendering operation - this is where the work of a DM/FB render pass
- // would occur in a real application
-
//////////////////////////////////////////////////////////////////////////////
-
- // TODO - reuse some of these constructs, no need to recreate everything
- // every time...
- context_t context;
- framebuffer_t *framebuffer = framebuffer_create_existing(idata->dmpixel,
idata->dm_width, idata->dm_height);
- float aspect = (float)idata->dm_width / (float)idata->dm_height;
- camera_t *camera = camera_create(CAMERA_POSITION, CAMERA_TARGET, aspect);
- context.framebuffer = framebuffer;
- context.camera = camera;
- scene_t *scene = blinn_lighthouse_scene();
- perframe_t perframe = test_build_perframe(scene, &context);
- test_draw_scene(scene, context.framebuffer, &perframe);
-
-
//////////////////////////////////////////////////////////////////////////////
-
//////////////////////////////////////////////////////////////////////////////
-
- // Update done - let the parent structure know. We don't clear the
- // render_needed flag here, since the parent window may have changed
- // between the start and the end of this render and if it has we need
- // to start over.
- Tcl_MutexLock(&dilock);
- idata->dm_render_running = 0;
- Tcl_MutexLock(&threadMutex);
- idata->dm_render_ready = 1;
- Tcl_MutexUnlock(&threadMutex);
- Tcl_MutexUnlock(&dilock);
-
- // Generate an event for the manager thread to let it know we're done
- Tcl_MutexLock(&threadMutex);
- struct RenderEvent *threadEventPtr = (struct RenderEvent
*)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = noop_proc;
- Tcl_ThreadQueueEvent(idata->dm_id, (Tcl_Event *) threadEventPtr,
TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->dm_id);
- Tcl_MutexUnlock(&threadMutex);
-
- // Render complete, we're done with this thread
- Tcl_ExitThread(TCL_OK);
- TCL_THREAD_CREATE_RETURN;
-}
-
-
-static Tcl_ThreadCreateType
-Fb_Render(ClientData clientData)
-{
- // Unpack the shared data object
- struct img_data *idata = (struct img_data *)clientData;
-
- // Lock updating of the idata values until we've gotten this run's key
- // information - we don't want this changing mid-render.
- Tcl_MutexLock(&fblock);
-
- // We're now in process - don't start another frame until this one
- // is complete.
- idata->fb_render_running = 1;
-
- // Until we're done, make sure nobody thinks we are ready
- Tcl_MutexLock(&threadMutex);
- idata->fb_render_ready = 0;
- Tcl_MutexUnlock(&threadMutex);
-
- // Lock in this width and height - that's what we've got memory for,
- // so that's what the remainder of this rendering pass will use.
- idata->fb_width = idata->dm_width;
- idata->fb_height = idata->dm_height;
-
- // If we have insufficient memory, allocate or reallocate a local
- // buffer big enough for the purpose. We use our own buffer for
- // rendering to allow Tk full control over what it wants to do behind
- // the scenes. We need this memory to persist, so handle it from the
- // management thread.
- long b_minsize = idata->fb_width * idata->fb_height * 4;
- if (!idata->fbpixel || idata->fb_buff_size < b_minsize) {
- idata->fbpixel = (unsigned char *)bu_realloc(idata->fbpixel,
2*b_minsize*sizeof(char), "realloc pixbuf");
- idata->fb_buff_size = b_minsize * 2;
- }
-
- // Have the key values now, we can unlock and allow interactivity in
- // the parent. We'll finish this rendering pass before paying any
- // further attention to parent settings, but the parent may now set
- // them for future consideration.
- Tcl_MutexUnlock(&fblock);
-
-
//////////////////////////////////////////////////////////////////////////////
- // Rendering operation - this is where the work of a FB render pass
- // would occur in a real application
-
//////////////////////////////////////////////////////////////////////////////
-
- int r = (*idata->colors)((*idata->gen));
- int g = (*idata->colors)((*idata->gen));
- int b = (*idata->colors)((*idata->gen));
-
- // Initialize. This alters the actual data, but Tcl/Tk doesn't know about
it yet.
- for (int i = 0; i < (idata->fb_width * idata->fb_height * 4); i+=4) {
- // Red
- idata->fbpixel[i] = (r) ? 255 : 0;
- // Green
- idata->fbpixel[i+1] = (g) ? 255 : 0;
- // Blue
- idata->fbpixel[i+2] = (b) ? 255 : 0;
- // Alpha
- idata->fbpixel[i+3] = 100;
- }
-
-
//////////////////////////////////////////////////////////////////////////////
-
//////////////////////////////////////////////////////////////////////////////
-
- Tcl_MutexLock(&fblock);
- idata->fb_render_running = 0;
- Tcl_MutexLock(&threadMutex);
- idata->fb_render_ready = 1;
- Tcl_MutexUnlock(&threadMutex);
- Tcl_MutexUnlock(&fblock);
-
- std::cout << "FB update: " << r << "," << g << "," << b << "\n";
-
- // Generate an event for the manager thread to let it know we're done, if
the
- // display manager isn't already about to generate such an event
- Tcl_MutexLock(&threadMutex);
- struct RenderEvent *threadEventPtr = (struct RenderEvent
*)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = noop_proc;
- Tcl_ThreadQueueEvent(idata->fb_id, (Tcl_Event *) threadEventPtr,
TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->fb_id);
- Tcl_MutexUnlock(&threadMutex);
-
- // Render complete, we're done with this thread
- Tcl_ExitThread(TCL_OK);
- TCL_THREAD_CREATE_RETURN;
-}
-
-
-static Tcl_ThreadCreateType
-Dm_Update_Manager(ClientData clientData)
-{
- // This thread needs to process events - give it its own interp so it can
do so.
- Tcl_Interp *interp = Tcl_CreateInterp();
- Tcl_Init(interp);
-
- // Unpack the shared data object
- struct img_data *idata = (struct img_data *)clientData;
-
- // Set up our data sources - we'll reuse these for each render, so they are
- // set up in the managing thread
- std::default_random_engine gen;
- std::uniform_int_distribution<int> colors(0,1);
- std::uniform_int_distribution<int> vals(0,255);
- idata->gen = &gen;
- idata->colors = &colors;
- idata->vals = &vals;
-
- // Until we're shutting down, check for and process events - this thread
will persist
- // for the life of the application.
- while (!idata->shutdown) {
-
- // Events can come from either the parent (requesting a rendering,
announcing a shutdown)
- // or from the rendering thread (announcing completion of a rendering.)
- Tcl_DoOneEvent(0);
-
- // If anyone flipped the shutdown switch while we were waiting on an
- // event, don't do any more work - there's no point.
- if (idata->shutdown) {
- continue;
- }
-
- // If we have a render ready, let the parent know to update the GUI
before we
- // go any further. Even if a completed render is out of data, it's
closer to
- // current than what was previously displayed so the application should
go ahead
- // and show it.
- if (idata->dm_render_ready) {
-
- // Generate an event for the parent thread - its time to update the
- // Tk_Photo in the GUI that's holding the image
- Tcl_MutexLock(&threadMutex);
- struct RenderEvent *threadEventPtr = (struct RenderEvent
*)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = UpdateProc;
- Tcl_ThreadQueueEvent(idata->parent_id, (Tcl_Event *)
threadEventPtr, TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->parent_id);
- Tcl_MutexUnlock(&threadMutex);
-
- // If we don't have any work pending, go back to waiting. If we
know
- // we're already out of date, proceed without waiting for another
event
- // from the parent - we've already had one.
- if (!idata->dm_render_needed) {
- continue;
- }
- }
-
- // If we're already rendering and we have another rendering event, it's
- // an update request from the parent - set the flag and continue
- if (idata->dm_render_running) {
- idata->dm_render_needed = 1;
- continue;
- }
-
- // If we need a render and aren't already running, it's time to go to
work.
-
-
- // Start a rendering thread.
- Tcl_ThreadId threadID;
- if (Tcl_CreateThread(&threadID, Dm_Render, (ClientData)idata,
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) {
- std::cerr << "can't create dm render thread\n";
- }
- }
-
- // We're well and truly done - the application is closing down - free the
- // rendering buffer and quit the thread
- if (idata->dmpixel) {
- bu_free(idata->dmpixel, "free pixbuf");
- }
- Tcl_ExitThread(TCL_OK);
- TCL_THREAD_CREATE_RETURN;
-}
-
-static Tcl_ThreadCreateType
-Fb_Update_Manager(ClientData clientData)
-{
- // This thread needs to process events - give it its own interp so it can
do so.
- Tcl_Interp *interp = Tcl_CreateInterp();
- Tcl_Init(interp);
-
- // Unpack the shared data object
- struct img_data *idata = (struct img_data *)clientData;
-
- // Until we're shutting down, check for and process events - this thread
will persist
- // for the life of the application.
- while (!idata->shutdown) {
-
- // Events can come from either the parent (requesting a rendering,
announcing a shutdown)
- // or from the rendering thread (announcing completion of a rendering.)
-
- // Eventually we'll want separate events to trigger this, but for now
just update it at
- // regular intervals
- if (!idata->fb_render_ready) {
- Tcl_Sleep(2000);
- }
- //Tcl_DoOneEvent(0);
-
-
- // If anyone flipped the shutdown switch while we were waiting, don't
- // do any more work - there's no point.
- if (idata->shutdown) {
- continue;
- }
-
- // If we have a render ready, let the parent know to update the GUI.
- if (idata->fb_render_ready) {
-
- // Generate an event for the parent thread - its time to update the
- // Tk_Photo in the GUI that's holding the image
- Tcl_MutexLock(&threadMutex);
- struct RenderEvent *threadEventPtr = (struct RenderEvent
*)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = UpdateProc;
- Tcl_ThreadQueueEvent(idata->parent_id, (Tcl_Event *)
threadEventPtr, TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->parent_id);
- Tcl_MutexUnlock(&threadMutex);
-
- // Go back to waiting.
- Tcl_MutexLock(&threadMutex);
- idata->fb_render_ready = 0;
- Tcl_MutexUnlock(&threadMutex);
- continue;
- }
-
- // If we need a render and aren't already running, it's time to go to
work.
- // Start a framebuffer thread.
- std::cout << "Framebuffer updating!\n";
- Tcl_ThreadId fbthreadID;
- if (Tcl_CreateThread(&fbthreadID, Fb_Render, (ClientData)idata,
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) {
- std::cerr << "can't create fb render thread\n";
- }
-
- }
-
- // We're well and truly done - the application is closing down - free the
- // rendering buffer and quit the thread
- if (idata->fbpixel) {
- bu_free(idata->fbpixel, "free pixbuf");
- }
- Tcl_ExitThread(TCL_OK);
- TCL_THREAD_CREATE_RETURN;
-}
-
-int
-main(int UNUSED(argc), const char *argv[])
-{
- std::string bind_cmd;
-
- bu_setprogname(argv[0]);
-
- scene_t *scene = blinn_lighthouse_scene();
- if (!scene)
- exit(1);
-
- // For now, just use a fixed 512x512 image size
- int wsize = 512;
-
- // Set up random image data generation so we can simulate a raytrace or
- // raster changing the view data. We need to use these for subsequent
- // image updating, so pack them into a structure we can pass through Tcl's
- // evaluations.
- struct img_data *idata;
- BU_GET(idata, struct img_data);
- idata->dm_render_needed = 1;
- idata->dm_render_running = 0;
- idata->dm_render_ready = 0;
- idata->shutdown = 0;
- idata->dm_buff_size = 0;
- idata->dmpixel = NULL;
- idata->fb_buff_size = 0;
- idata->fbpixel = NULL;
- idata->parent_id = Tcl_GetCurrentThread();
-
- // Set up Tcl/Tk
- Tcl_FindExecutable(argv[0]);
- Tcl_Interp *interp = Tcl_CreateInterp();
- Tcl_Init(interp);
- Tk_Init(interp);
-
- // Save a pointer so we can get at the interp later
- idata->parent_interp = interp;
-
- // Make a simple toplevel window
- Tk_Window tkwin = Tk_MainWindow(interp);
- Tk_GeometryRequest(tkwin, wsize, wsize);
- Tk_MakeWindowExist(tkwin);
- Tk_MapWindow(tkwin);
-
- // Create the (initially empty) Tcl/Tk Photo (a.k.a color image) we will
- // use as our rendering canvas for the images.
- //
- // Note: confirmed with Tcl/Tk community that (at least as of Tcl/Tk 8.6)
- // Tcl_Eval is the ONLY way to create an image object. The C API doesn't
- // expose that ability, although it does support manipulation of the
- // created object.
- std::string img_cmd = std::string("image create photo ") +
std::string(DM_PHOTO);
- Tcl_Eval(interp, img_cmd.c_str());
- Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
- Tk_PhotoBlank(dm_img);
- Tk_PhotoSetSize(interp, dm_img, wsize, wsize);
-
- // Initialize the PhotoImageBlock information for a color image of size
- // 500x500 pixels.
- Tk_PhotoImageBlock dm_data;
- dm_data.width = wsize;
- dm_data.height = wsize;
- dm_data.pixelSize = 4;
- dm_data.pitch = wsize * 4;
- dm_data.offset[0] = 0;
- dm_data.offset[1] = 1;
- dm_data.offset[2] = 2;
- dm_data.offset[3] = 3;
-
- // Actually create our memory for the image buffer. Expects RGBA
information
- dm_data.pixelPtr = (unsigned char *)Tcl_AttemptAlloc(dm_data.width *
dm_data.height * 4);
- if (!dm_data.pixelPtr) {
- std::cerr << "Tcl/Tk photo memory allocation failed!\n";
- exit(1);
- }
-
- // Initialize. This alters the actual data, but Tcl/Tk doesn't know about
it yet.
- for (int i = 0; i < (dm_data.width * dm_data.height * 4); i+=4) {
- // Red
- dm_data.pixelPtr[i] = 0;
- // Green
- dm_data.pixelPtr[i+1] = 255;
- // Blue
- dm_data.pixelPtr[i+2] = 0;
- // Alpha at 255 - we dont' want transparency for this demo.
- dm_data.pixelPtr[i+3] = 255;
- }
-
- // Let Tk_Photo know we have data
- Tk_PhotoPutBlock(interp, dm_img, &dm_data, 0, 0, dm_data.width,
dm_data.height, TK_PHOTO_COMPOSITE_SET);
-
- // We now have a window - set the default idata size
- idata->dm_width = dm_data.width;
- idata->dm_height = dm_data.height;
-
- // Define a canvas widget, pack it into the root window, and associate the
image with it
- // TODO - should the canvas be inside a frame?
- std::string canvas_cmd = std::string("canvas ") + std::string(DM_CANVAS) +
std::string(" -width ") + std::to_string(wsize) + std::string(" -height ") +
std::to_string(wsize) + std::string(" -borderwidth 0");
- Tcl_Eval(interp, canvas_cmd.c_str());
- std::string pack_cmd = std::string("pack ") + std::string(DM_CANVAS) +
std::string(" -fill both -expand 1");
- Tcl_Eval(interp, pack_cmd.c_str());
- std::string canvas_img_cmd = std::string(DM_CANVAS) + std::string(" create
image 0 0 -image ") + std::string(DM_PHOTO) + std::string(" -anchor nw");
- Tcl_Eval(interp, canvas_img_cmd.c_str());
-
-
- // Register a paint command so we can change the image contents near the
cursor position
- (void)Tcl_CreateCommand(interp, "image_paint", (Tcl_CmdProc
*)image_paint_xy, (ClientData)idata, (Tcl_CmdDeleteProc* )NULL);
- // Establish the Button-1+Motion combination event as the trigger for
drawing on the image
- bind_cmd = std::string("bind . <B1-Motion> {image_paint %x %y}");
- Tcl_Eval(interp, bind_cmd.c_str());
-
-
- // Register an update command so we can change the image contents
- (void)Tcl_CreateCommand(interp, "image_update", (Tcl_CmdProc
*)image_update_data, (ClientData)idata, (Tcl_CmdDeleteProc* )NULL);
- // Establish the Button-2 and Button-3 event as triggers for updating the
image contents
- bind_cmd = std::string("bind . <Button-2> {image_update}");
- Tcl_Eval(interp, bind_cmd.c_str());
- bind_cmd = std::string("bind . <Button-3> {image_update}");
- Tcl_Eval(interp, bind_cmd.c_str());
-
- // Update the photo if the window changes
- bind_cmd = std::string("bind ") + std::string(DM_CANVAS) + std::string("
<Configure> {image_update [winfo width %W] [winfo height %W]\"}");
- Tcl_Eval(interp, bind_cmd.c_str());
-
- // Multithreading experiment
- Tcl_ThreadId threadID;
- if (Tcl_CreateThread(&threadID, Dm_Update_Manager, (ClientData)idata,
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
- std::cerr << "can't create thread\n";
- }
- idata->dm_id = threadID;
-
- Tcl_ThreadId fbthreadID;
- if (Tcl_CreateThread(&fbthreadID, Fb_Update_Manager, (ClientData)idata,
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
- std::cerr << "can't create thread\n";
- }
- idata->fb_id = fbthreadID;
-
-
- // Enter the main applicatio loop - the initial image will appear, and
Button-1 mouse
- // clicks on the window should generate and display new images
- while (1) {
- Tcl_DoOneEvent(0);
- if (!Tk_GetNumMainWindows()) {
- int tret;
-
- // If we've closed the window, we're done - start spreading the word
- idata->shutdown = 1;
-
-
- // After setting the shutdown flag, send an event to wake up the FB
update manager thread.
- // It will see the change in status and proceed to shut itself down.
- Tcl_MutexLock(&fblock);
- struct RenderEvent *threadEventPtr = (struct RenderEvent
*)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = noop_proc;
- Tcl_ThreadQueueEvent(idata->fb_id, (Tcl_Event *) threadEventPtr,
TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->fb_id);
- Tcl_MutexUnlock(&fblock);
-
- // Wait for the thread cleanup to complete
- Tcl_JoinThread(idata->fb_id, &tret);
- if (tret != TCL_OK) {
- std::cerr << "Failed to shut down framebuffer management
thread\n";
- } else {
- std::cout << "Successful framebuffer management thread
shutdown\n";
- }
-
- // After setting the shutdown flag, send an event to wake up the
update manager thread.
- // It will see the change in status and proceed to shut itself down.
- Tcl_MutexLock(&dilock);
- threadEventPtr = (struct RenderEvent *)ckalloc(sizeof(RenderEvent));
- threadEventPtr->idata = idata;
- threadEventPtr->event.proc = noop_proc;
- Tcl_ThreadQueueEvent(idata->dm_id, (Tcl_Event *) threadEventPtr,
TCL_QUEUE_TAIL);
- Tcl_ThreadAlert(idata->dm_id);
- Tcl_MutexUnlock(&dilock);
-
- // Wait for the thread cleanup to complete
- Tcl_JoinThread(idata->dm_id, &tret);
- if (tret != TCL_OK) {
- std::cerr << "Failed to shut down display management thread\n";
- } else {
- std::cout << "Successful display management thread shutdown\n";
- }
-
-
- // Clean up memory and exit
- BU_PUT(idata, struct img_data);
- exit(0);
- }
- }
-}
-
-// Local Variables:
-// tab-width: 8
-// mode: C++
-// c-basic-offset: 4
-// indent-tabs-mode: t
-// c-file-style: "stroustrup"
-// End:
-// ex: shiftwidth=4 tabstop=8
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
_______________________________________________
BRL-CAD Source Commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/brlcad-commits