Add Chromium-only Blender WebEngine parity work
This commit is contained in:
96
blender-5.2.0/source/blender/render/CMakeLists.txt
Normal file
96
blender-5.2.0/source/blender/render/CMakeLists.txt
Normal file
@@ -0,0 +1,96 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
intern
|
||||
../compositor
|
||||
../compositor/cached_resources
|
||||
../compositor/derived_resources
|
||||
../draw/intern
|
||||
../gpu/intern
|
||||
../makesrna
|
||||
../simulation
|
||||
../../../intern/mikktspace
|
||||
../../../intern/mantaflow/extern
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/bake.cc
|
||||
intern/compositor.cc
|
||||
intern/engine.cc
|
||||
intern/initrender.cc
|
||||
intern/multires_bake.cc
|
||||
intern/pipeline.cc
|
||||
intern/render_result.cc
|
||||
intern/render_types.cc
|
||||
intern/texture_image.cc
|
||||
intern/texture_margin.cc
|
||||
intern/texture_procedural.cc
|
||||
intern/tile_highlight.cc
|
||||
intern/zbuf.cc
|
||||
|
||||
RE_bake.h
|
||||
RE_compositor.hh
|
||||
RE_engine.h
|
||||
RE_multires_bake.h
|
||||
RE_pipeline.h
|
||||
RE_texture.h
|
||||
RE_texture_margin.h
|
||||
|
||||
intern/pipeline.hh
|
||||
intern/render_result.h
|
||||
intern/render_types.h
|
||||
intern/texture_common.h
|
||||
intern/tile_highlight.h
|
||||
intern/zbuf.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::draw
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::imbuf::movie
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
bf_compositor
|
||||
bf_imbuf_openexr
|
||||
PRIVATE bf::intern::atomic
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::sequencer
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
if(WITH_PYTHON)
|
||||
add_definitions(-DWITH_PYTHON)
|
||||
list(APPEND INC
|
||||
../python
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_FREESTYLE)
|
||||
list(APPEND INC
|
||||
../freestyle
|
||||
)
|
||||
list(APPEND LIB
|
||||
bf_freestyle
|
||||
)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_HYDRA)
|
||||
add_subdirectory(hydra)
|
||||
endif()
|
||||
|
||||
blender_add_lib_nolist(bf_render "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::render ALIAS bf_render)
|
||||
144
blender-5.2.0/source/blender/render/RE_bake.h
Normal file
144
blender-5.2.0/source/blender/render/RE_bake.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct ImBuf;
|
||||
struct Mesh;
|
||||
struct Render;
|
||||
|
||||
struct BakeImage {
|
||||
struct Image *image;
|
||||
int tile_number;
|
||||
float uv_offset[2];
|
||||
int width;
|
||||
int height;
|
||||
size_t offset;
|
||||
|
||||
/* For associating render result layer with image. */
|
||||
char render_layer_name[RE_MAXNAME];
|
||||
};
|
||||
|
||||
struct BakeTargets {
|
||||
/* All images of the object. */
|
||||
BakeImage *images;
|
||||
int images_num;
|
||||
|
||||
/* Lookup table from Material number to BakeImage. */
|
||||
struct Image **material_to_image;
|
||||
int materials_num;
|
||||
|
||||
/* Pixel buffer to bake to. */
|
||||
float *result;
|
||||
int pixels_num;
|
||||
int channels_num;
|
||||
|
||||
/* Baking to non-color data image. */
|
||||
bool is_noncolor;
|
||||
};
|
||||
|
||||
struct BakePixel {
|
||||
int primitive_id, object_id;
|
||||
int seed;
|
||||
float uv[2];
|
||||
float du_dx, du_dy;
|
||||
float dv_dx, dv_dy;
|
||||
};
|
||||
|
||||
struct BakeHighPolyData {
|
||||
struct Object *ob;
|
||||
struct Object *ob_eval;
|
||||
struct Mesh *mesh;
|
||||
bool is_flip_object;
|
||||
|
||||
float obmat[4][4];
|
||||
float imat[4][4];
|
||||
};
|
||||
|
||||
/* `external_engine.cc` */
|
||||
|
||||
bool RE_bake_has_engine(const struct Render *re);
|
||||
|
||||
bool RE_bake_engine(struct Render *re,
|
||||
struct Depsgraph *depsgraph,
|
||||
struct Object *object,
|
||||
int object_id,
|
||||
const BakePixel pixel_array[],
|
||||
const BakeTargets *targets,
|
||||
eScenePassType pass_type,
|
||||
int pass_filter,
|
||||
float result[]);
|
||||
|
||||
/* `bake.cc` */
|
||||
|
||||
int RE_pass_depth(eScenePassType pass_type);
|
||||
|
||||
bool RE_bake_pixels_populate_from_objects(struct Mesh *me_low,
|
||||
BakePixel pixel_array_from[],
|
||||
BakePixel pixel_array_to[],
|
||||
BakeHighPolyData highpoly[],
|
||||
int highpoly_num,
|
||||
size_t pixels_num,
|
||||
bool is_custom_cage,
|
||||
float cage_extrusion,
|
||||
float max_ray_distance,
|
||||
const float mat_low[4][4],
|
||||
const float mat_cage[4][4],
|
||||
struct Mesh *me_cage);
|
||||
|
||||
void RE_bake_pixels_populate(struct Mesh *mesh,
|
||||
struct BakePixel *pixel_array,
|
||||
size_t pixels_num,
|
||||
const struct BakeTargets *targets,
|
||||
StringRef uv_layer);
|
||||
|
||||
void RE_bake_mask_fill(const BakePixel pixel_array[], size_t pixels_num, char *mask);
|
||||
|
||||
void RE_bake_margin(struct ImBuf *ibuf,
|
||||
char *mask,
|
||||
int margin,
|
||||
char margin_type,
|
||||
const Mesh *mesh,
|
||||
StringRef uv_layer,
|
||||
const float uv_offset[2]);
|
||||
|
||||
void RE_bake_normal_world_to_object(const BakePixel pixel_array[],
|
||||
size_t pixels_num,
|
||||
int depth,
|
||||
float result[],
|
||||
struct Object *ob,
|
||||
const eBakeNormalSwizzle normal_swizzle[3]);
|
||||
/**
|
||||
* This function converts an object space normal map
|
||||
* to a tangent space normal map for a given low poly mesh.
|
||||
*/
|
||||
void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[],
|
||||
size_t pixels_num,
|
||||
int depth,
|
||||
float result[],
|
||||
struct Mesh *mesh,
|
||||
const eBakeNormalSwizzle normal_swizzle[3],
|
||||
const float mat[4][4]);
|
||||
void RE_bake_normal_world_to_world(const BakePixel pixel_array[],
|
||||
size_t pixels_num,
|
||||
int depth,
|
||||
float result[],
|
||||
const eBakeNormalSwizzle normal_swizzle[3]);
|
||||
|
||||
void RE_bake_ibuf_clear(struct Image *image, bool is_tangent);
|
||||
|
||||
} // namespace blender
|
||||
46
blender-5.2.0/source/blender/render/RE_compositor.hh
Normal file
46
blender-5.2.0/source/blender/render/RE_compositor.hh
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace compositor {
|
||||
class RenderContext;
|
||||
enum class NodeGroupOutputTypes : uint8_t;
|
||||
} // namespace compositor
|
||||
|
||||
struct bNodeTree;
|
||||
struct Render;
|
||||
struct Main;
|
||||
struct RenderData;
|
||||
struct Scene;
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Render Compositor
|
||||
*
|
||||
* Implementation of the compositor for final rendering, as opposed to the viewport compositor
|
||||
* that is part of the draw manager. The input and output of this is pre-existing RenderResult
|
||||
* buffers in scenes, that are uploaded to and read back from the GPU. */
|
||||
|
||||
namespace render {
|
||||
class Compositor;
|
||||
}
|
||||
|
||||
/* Execute compositor. */
|
||||
void RE_compositor_execute(Render &render,
|
||||
const Main &main,
|
||||
const Scene &scene,
|
||||
const RenderData &render_data,
|
||||
const bNodeTree &node_tree,
|
||||
const char *view_name,
|
||||
compositor::RenderContext *render_context,
|
||||
compositor::NodeGroupOutputTypes needed_outputs);
|
||||
|
||||
/* Free compositor caches. */
|
||||
void RE_compositor_free(Render &render);
|
||||
|
||||
} // namespace blender
|
||||
294
blender-5.2.0/source/blender/render/RE_engine.h
Normal file
294
blender-5.2.0/source/blender/render/RE_engine.h
Normal file
@@ -0,0 +1,294 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "RE_bake.h"
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "BLI_threads.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BakePixel;
|
||||
struct BakeTargets;
|
||||
struct bNode;
|
||||
struct bNodeTree;
|
||||
struct Depsgraph;
|
||||
struct GPUContext;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct Render;
|
||||
struct RenderData;
|
||||
struct RenderEngine;
|
||||
struct RenderEngineType;
|
||||
struct RenderLayer;
|
||||
struct RenderPass;
|
||||
struct RenderResult;
|
||||
struct ReportList;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct ViewRender;
|
||||
|
||||
/* External Engine */
|
||||
|
||||
/** #RenderEngineType.flag */
|
||||
enum RenderEngineTypeFlag {
|
||||
RE_INTERNAL = (1 << 0),
|
||||
RE_USE_PREVIEW = (1 << 1),
|
||||
RE_USE_POSTPROCESS = (1 << 2),
|
||||
RE_USE_EEVEE_VIEWPORT = (1 << 3),
|
||||
RE_USE_SHADING_NODES_CUSTOM = (1 << 4),
|
||||
RE_USE_SPHERICAL_STEREO = (1 << 5),
|
||||
RE_USE_STEREO_VIEWPORT = (1 << 6),
|
||||
RE_USE_GPU_CONTEXT = (1 << 7),
|
||||
RE_USE_CUSTOM_FREESTYLE = (1 << 8),
|
||||
RE_USE_NO_IMAGE_SAVE = (1 << 9),
|
||||
RE_USE_MATERIALX = (1 << 10),
|
||||
};
|
||||
|
||||
/** #RenderEngine.flag */
|
||||
enum RenderEngineFlag {
|
||||
RE_ENGINE_ANIMATION = (1 << 0),
|
||||
RE_ENGINE_PREVIEW = (1 << 1),
|
||||
RE_ENGINE_DO_DRAW = (1 << 2),
|
||||
RE_ENGINE_DO_UPDATE = (1 << 3),
|
||||
RE_ENGINE_RENDERING = (1 << 4),
|
||||
RE_ENGINE_HIGHLIGHT_TILES = (1 << 5),
|
||||
RE_ENGINE_CAN_DRAW = (1 << 6),
|
||||
};
|
||||
|
||||
extern ListBaseT<RenderEngineType> R_engines;
|
||||
|
||||
struct RenderEngineType {
|
||||
struct RenderEngineType *next, *prev;
|
||||
|
||||
/* Type info. */
|
||||
char idname[/*BKE_ST_MAXNAME*/ 64];
|
||||
char name[64];
|
||||
int flag;
|
||||
|
||||
void (*update)(struct RenderEngine *engine, struct Main *bmain, struct Depsgraph *depsgraph);
|
||||
|
||||
void (*render)(struct RenderEngine *engine, struct Depsgraph *depsgraph);
|
||||
|
||||
/* Offline rendering is finished - no more view layers will be rendered.
|
||||
*
|
||||
* All the pending data is to be communicated from the engine back to Blender. In a possibly
|
||||
* most memory-efficient manner (engine might free its database before making Blender to allocate
|
||||
* full-frame render result). */
|
||||
void (*render_frame_finish)(struct RenderEngine *engine);
|
||||
|
||||
void (*draw)(struct RenderEngine *engine,
|
||||
const struct bContext *context,
|
||||
struct Depsgraph *depsgraph);
|
||||
|
||||
void (*bake)(struct RenderEngine *engine,
|
||||
struct Depsgraph *depsgraph,
|
||||
struct Object *object,
|
||||
int pass_type,
|
||||
int pass_filter,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
void (*view_update)(struct RenderEngine *engine,
|
||||
const struct bContext *context,
|
||||
struct Depsgraph *depsgraph);
|
||||
void (*view_draw)(struct RenderEngine *engine,
|
||||
const struct bContext *context,
|
||||
struct Depsgraph *depsgraph);
|
||||
|
||||
void (*update_script_node)(struct RenderEngine *engine,
|
||||
struct bNodeTree *ntree,
|
||||
struct bNode *node);
|
||||
void (*update_render_passes)(struct RenderEngine *engine,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer);
|
||||
void (*update_custom_camera)(struct RenderEngine *engine, struct Camera *cam);
|
||||
|
||||
struct DrawEngineType *draw_engine;
|
||||
|
||||
/* RNA integration */
|
||||
ExtensionRNA rna_ext;
|
||||
};
|
||||
|
||||
using update_render_passes_cb_t = void (*)(void *userdata,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
const char *name,
|
||||
int channels,
|
||||
const char *chanid,
|
||||
eNodeSocketDatatype type);
|
||||
|
||||
struct RenderEngine {
|
||||
RenderEngineType *type;
|
||||
void *py_instance;
|
||||
|
||||
int flag;
|
||||
struct Object *camera_override;
|
||||
unsigned int layer_override;
|
||||
|
||||
struct Render *re;
|
||||
ListBaseT<RenderResult> fullresult;
|
||||
char text[/*IMA_MAX_RENDER_TEXT_SIZE*/ 512];
|
||||
|
||||
int resolution_x, resolution_y;
|
||||
|
||||
struct ReportList *reports;
|
||||
|
||||
struct {
|
||||
const struct BakeTargets *targets;
|
||||
const struct BakePixel *pixels;
|
||||
float *result;
|
||||
int image_id;
|
||||
int object_id;
|
||||
} bake;
|
||||
|
||||
/* Depsgraph */
|
||||
struct Depsgraph *depsgraph;
|
||||
bool has_grease_pencil;
|
||||
|
||||
/* callback for render pass query */
|
||||
ThreadMutex update_render_passes_mutex;
|
||||
update_render_passes_cb_t update_render_passes_cb;
|
||||
void *update_render_passes_data;
|
||||
|
||||
/* GPU context. */
|
||||
GHOST_IContext *system_gpu_context; /* WindowManager GPU context -> GHOSTContext. */
|
||||
ThreadMutex blender_gpu_context_mutex;
|
||||
bool use_drw_render_context;
|
||||
struct GPUContext *blender_gpu_context;
|
||||
/* Whether to restore DRWState after RenderEngine display pass. */
|
||||
bool gpu_restore_context;
|
||||
};
|
||||
|
||||
RenderEngine *RE_engine_create(RenderEngineType *type);
|
||||
void RE_engine_free(RenderEngine *engine);
|
||||
|
||||
/**
|
||||
* Loads in image into a result, size must match
|
||||
* x/y offsets are only used on a partial copy when dimensions don't match.
|
||||
*/
|
||||
void RE_layer_load_from_file(
|
||||
struct RenderLayer *layer, struct ReportList *reports, const char *filepath, int x, int y);
|
||||
void RE_result_load_from_file(struct RenderResult *result,
|
||||
struct ReportList *reports,
|
||||
const char *filepath);
|
||||
|
||||
struct RenderResult *RE_engine_begin_result(
|
||||
RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname);
|
||||
void RE_engine_update_result(RenderEngine *engine, struct RenderResult *result);
|
||||
void RE_engine_add_pass(RenderEngine *engine,
|
||||
const char *name,
|
||||
int channels,
|
||||
const char *chan_id,
|
||||
const char *layername);
|
||||
void RE_engine_end_result(RenderEngine *engine,
|
||||
struct RenderResult *result,
|
||||
bool cancel,
|
||||
bool highlight,
|
||||
bool merge_results);
|
||||
struct RenderResult *RE_engine_get_result(struct RenderEngine *engine);
|
||||
|
||||
struct RenderPass *RE_engine_pass_by_index_get(struct RenderEngine *engine,
|
||||
const char *layer_name,
|
||||
int index);
|
||||
|
||||
const char *RE_engine_active_view_get(RenderEngine *engine);
|
||||
void RE_engine_active_view_set(RenderEngine *engine, const char *viewname);
|
||||
float RE_engine_get_camera_shift_x(RenderEngine *engine,
|
||||
struct Object *camera,
|
||||
bool use_spherical_stereo);
|
||||
void RE_engine_get_camera_model_matrix(RenderEngine *engine,
|
||||
struct Object *camera,
|
||||
bool use_spherical_stereo,
|
||||
float r_modelmat[16]);
|
||||
bool RE_engine_get_spherical_stereo(RenderEngine *engine, struct Object *camera);
|
||||
|
||||
bool RE_engine_test_break(RenderEngine *engine);
|
||||
void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info);
|
||||
void RE_engine_update_progress(RenderEngine *engine, float progress);
|
||||
void RE_engine_update_memory_stats(RenderEngine *engine, float mem_used, float mem_peak);
|
||||
void RE_engine_report(RenderEngine *engine, int type, const char *msg);
|
||||
void RE_engine_set_error_message(RenderEngine *engine, const char *msg);
|
||||
|
||||
bool RE_engine_render(struct Render *re, bool do_all);
|
||||
|
||||
bool RE_engine_is_external(const struct Render *re);
|
||||
|
||||
void RE_engine_frame_set(struct RenderEngine *engine, int frame, float subframe);
|
||||
|
||||
void RE_engine_update_render_passes(struct RenderEngine *engine,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
update_render_passes_cb_t callback,
|
||||
void *callback_data);
|
||||
void RE_engine_register_pass(struct RenderEngine *engine,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *view_layer,
|
||||
const char *name,
|
||||
int channels,
|
||||
const char *chanid,
|
||||
eNodeSocketDatatype type);
|
||||
|
||||
bool RE_engine_use_persistent_data(struct RenderEngine *engine);
|
||||
|
||||
struct RenderEngine *RE_engine_get(const struct Render *re);
|
||||
struct RenderEngine *RE_view_engine_get(const struct ViewRender *view_render);
|
||||
|
||||
/**
|
||||
* Acquire render engine for drawing via its `draw()` callback.
|
||||
*
|
||||
* If drawing is not possible false is returned. If drawing is possible then the engine is
|
||||
* "acquired" so that it can not be freed by the render pipeline.
|
||||
*
|
||||
* Drawing is possible if the engine has the `draw()` callback and it is in its `render()`
|
||||
* callback.
|
||||
*/
|
||||
bool RE_engine_draw_acquire(struct Render *re);
|
||||
void RE_engine_draw_release(struct Render *re);
|
||||
|
||||
/**
|
||||
* GPU context for engine to create and update GPU resources in its own thread,
|
||||
* without blocking the main thread. Used by Cycles' display driver to create
|
||||
* display textures.
|
||||
*/
|
||||
bool RE_engine_gpu_context_create(struct RenderEngine *engine);
|
||||
void RE_engine_gpu_context_destroy(struct RenderEngine *engine);
|
||||
|
||||
bool RE_engine_gpu_context_enable(struct RenderEngine *engine);
|
||||
void RE_engine_gpu_context_disable(struct RenderEngine *engine);
|
||||
|
||||
void RE_engine_gpu_context_lock(struct RenderEngine *engine);
|
||||
void RE_engine_gpu_context_unlock(struct RenderEngine *engine);
|
||||
|
||||
/* Engine Types */
|
||||
|
||||
void RE_engines_init();
|
||||
void RE_engines_exit();
|
||||
void RE_engines_register(RenderEngineType *render_type);
|
||||
|
||||
RenderEngineType *RE_engines_find(const char *idname);
|
||||
bool RE_engines_is_registered(const char *idname);
|
||||
|
||||
const rcti *RE_engine_get_current_tiles(struct Render *re, int *r_total_tiles);
|
||||
struct RenderData *RE_engine_get_render_data(struct Render *re);
|
||||
void RE_bake_engine_set_engine_parameters(struct Render *re,
|
||||
struct Main *bmain,
|
||||
struct Scene *scene);
|
||||
|
||||
void RE_engine_free_blender_memory(struct RenderEngine *engine);
|
||||
|
||||
void RE_engine_tile_highlight_set(
|
||||
struct RenderEngine *engine, int x, int y, int width, int height, bool highlight);
|
||||
void RE_engine_tile_highlight_clear_all(struct RenderEngine *engine);
|
||||
|
||||
} // namespace blender
|
||||
59
blender-5.2.0/source/blender/render/RE_multires_bake.h
Normal file
59
blender-5.2.0/source/blender/render/RE_multires_bake.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2010 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Image;
|
||||
struct Mesh;
|
||||
struct MultiresBakeRender;
|
||||
struct MultiresModifierData;
|
||||
|
||||
struct MultiresBakeRender {
|
||||
/* Base mesh at the input of the multiresolution modifier and data of the modifier which is being
|
||||
* baked. */
|
||||
Mesh *base_mesh = nullptr;
|
||||
MultiresModifierData *multires_modifier = nullptr;
|
||||
|
||||
int bake_margin = 0;
|
||||
eBakeMarginType bake_margin_type = R_BAKE_ADJACENT_FACES;
|
||||
eBakeType type = R_BAKE_NORMALS;
|
||||
eBakeSpace displacement_space = R_BAKE_SPACE_OBJECT;
|
||||
|
||||
/* Use low-resolution mesh when baking displacement maps.
|
||||
* When true displacement is calculated between the final position in the SubdivCCG and the
|
||||
* corresponding location on the base mesh.
|
||||
* When false displacement is calculated between the final position in the SubdivCCG and the
|
||||
* multiresolution modifier calculated at the bake level, further subdivided (without adding
|
||||
* displacement) to the final multi-resolution level. */
|
||||
bool use_low_resolution_mesh = false;
|
||||
|
||||
/* Material aligned image array (for per-face bake image), */
|
||||
Vector<Image *> ob_image;
|
||||
|
||||
Set<Image *> images;
|
||||
|
||||
int num_total_objects = 0;
|
||||
int num_baked_objects = 0;
|
||||
|
||||
bool *stop = nullptr;
|
||||
bool *do_update = nullptr;
|
||||
float *progress = nullptr;
|
||||
};
|
||||
|
||||
void RE_multires_bake_images(MultiresBakeRender &bake);
|
||||
|
||||
} // namespace blender
|
||||
512
blender-5.2.0/source/blender/render/RE_pipeline.h
Normal file
512
blender-5.2.0/source/blender/render/RE_pipeline.h
Normal file
@@ -0,0 +1,512 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
class GHOST_IContext;
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
struct ExrHandle;
|
||||
struct ImBuf;
|
||||
struct Image;
|
||||
struct ImageFormatData;
|
||||
struct MovieWriter;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct RenderData;
|
||||
struct RenderResult;
|
||||
struct ReportList;
|
||||
struct Scene;
|
||||
struct StampData;
|
||||
struct ViewLayer;
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
/* this include is what is exposed of render to outside world */
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
/* length of the scene name + passname */
|
||||
#define RE_MAXNAME ((MAX_ID_NAME - 2) + 10)
|
||||
|
||||
/* only used as handle */
|
||||
struct RenderView {
|
||||
struct RenderView *next, *prev;
|
||||
char name[/*EXR_VIEW_MAXNAME*/ 64];
|
||||
|
||||
/**
|
||||
* Image buffer of a composited layer or a sequencer output.
|
||||
* The `ibuf` is only allocated if it has an actual data in one of its buffers
|
||||
* (float, byte, or GPU).
|
||||
*/
|
||||
struct ImBuf *ibuf;
|
||||
};
|
||||
|
||||
struct RenderPass {
|
||||
struct RenderPass *next, *prev;
|
||||
int channels;
|
||||
char name[/*EXR_PASS_MAXNAME*/ 64];
|
||||
char chan_id[/*EXR_PASS_MAXCHAN*/ 24];
|
||||
|
||||
/**
|
||||
* Image buffer which contains data of this pass.
|
||||
*
|
||||
* The data can be either CPU side stored in ibuf->float_buffer, or a GPU-side stored in
|
||||
* ibuf->gpu (during rendering, i.e.).
|
||||
*
|
||||
* The pass data storage is lazily allocated, and until data is actually provided
|
||||
* (via either CPU buffer of GPU texture) the ibuf is not allocated.
|
||||
*/
|
||||
struct ImBuf *ibuf;
|
||||
|
||||
int rectx, recty;
|
||||
|
||||
char fullname[/*EXR_PASS_MAXNAME*/ 64];
|
||||
char view[/*EXR_VIEW_MAXNAME*/ 64];
|
||||
/** Quick lookup. */
|
||||
int view_id;
|
||||
|
||||
char _pad0[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* - A render-layer is a full image, but with all passes and samples.
|
||||
* - The size of the rects is defined in #RenderResult.
|
||||
* - After render, the Combined pass is in combined,
|
||||
* for render-layers read from files it is a real pass.
|
||||
*/
|
||||
struct RenderLayer {
|
||||
struct RenderLayer *next, *prev;
|
||||
|
||||
/** copy of RenderData */
|
||||
char name[RE_MAXNAME];
|
||||
int layflag, passflag, pass_xor;
|
||||
|
||||
int rectx, recty;
|
||||
|
||||
ListBaseT<RenderPass> passes;
|
||||
};
|
||||
|
||||
struct RenderResult {
|
||||
struct RenderResult *next = nullptr, *prev = nullptr;
|
||||
|
||||
/* The number of users of this render result. Default value is 0. The result is freed when
|
||||
* #RE_FreeRenderResult is called with the render result with 0 users. In a way this is
|
||||
* off-by-one, but it is the easiest for the currently used zero-initialized state. The way to
|
||||
* think of it is the number of extra users.
|
||||
*
|
||||
* TODO: Make it an actual number of users, so the #RE_FreeRenderResult frees the result when
|
||||
* the number of users goes to 0.
|
||||
*
|
||||
* TODO: Make it atomic. Currently it is not to allow shallow copying. */
|
||||
int user_counter = 0;
|
||||
|
||||
/* target image size */
|
||||
int rectx = 0, recty = 0;
|
||||
|
||||
/* The temporary storage to pass image data from #RE_AcquireResultImage.
|
||||
* Is null pointer when the RenderResult is not coming from the #RE_AcquireResultImage, and is
|
||||
* a pointer to an existing ibuf in either RenderView or a RenderPass otherwise. */
|
||||
struct ImBuf *ibuf = nullptr;
|
||||
|
||||
/* coordinates within final image (after cropping) */
|
||||
rcti tilerect;
|
||||
|
||||
/* the main buffers */
|
||||
ListBaseT<RenderLayer> layers = {};
|
||||
|
||||
/* multiView maps to a StringVector in OpenEXR */
|
||||
ListBaseT<RenderView> views = {};
|
||||
|
||||
/* Render layer to display. */
|
||||
RenderLayer *renlay = nullptr;
|
||||
|
||||
/* for render results in Image, verify validity for sequences */
|
||||
int framenr = 0;
|
||||
|
||||
/**
|
||||
* Pixels per meter (for image output).
|
||||
* - Typically initialized via #BKE_scene_ppm_get.
|
||||
* - May be zero which indicates the PPM being "unset".
|
||||
* Although in most cases a scene is available.
|
||||
*/
|
||||
double ppm[2];
|
||||
|
||||
/* for acquire image, to indicate if it there is a combined layer */
|
||||
bool have_combined = false;
|
||||
|
||||
/* render info text */
|
||||
char *text = nullptr;
|
||||
char *error = nullptr;
|
||||
|
||||
struct StampData *stamp_data = nullptr;
|
||||
|
||||
bool passes_allocated = false;
|
||||
};
|
||||
|
||||
struct RenderStats {
|
||||
int cfra;
|
||||
bool localview;
|
||||
double starttime, lastframetime;
|
||||
const char *infostr, *statstr;
|
||||
char scene_name[MAX_ID_NAME - 2];
|
||||
int mem_used, mem_peak;
|
||||
};
|
||||
|
||||
/* *********************** API ******************** */
|
||||
|
||||
/**
|
||||
* The owner is a unique identifier for the render, either an original scene
|
||||
* datablock for regular renders, or an area for preview renders.
|
||||
* Calling a new render with an existing owner frees the existing render. */
|
||||
struct Render *RE_NewRender(const void *owner);
|
||||
struct Render *RE_GetRender(const void *owner);
|
||||
|
||||
struct Scene;
|
||||
struct Render *RE_NewSceneRender(const struct Scene *scene);
|
||||
struct Render *RE_GetSceneRender(const struct Scene *scene);
|
||||
|
||||
struct RenderEngineType;
|
||||
struct ViewRender *RE_NewViewRender(struct RenderEngineType *engine_type);
|
||||
|
||||
/* Creates a new render for interactive compositing of the given scene. If an existing render
|
||||
* exists for the given scene, it is returned instead. See interactive_compositor_renders in
|
||||
* RenderGlobal for more information. */
|
||||
struct Render *RE_NewInteractiveCompositorRender(const struct Scene *scene);
|
||||
|
||||
/* Assign default dummy callbacks. */
|
||||
|
||||
/**
|
||||
* Use free render as signal to do everything over (previews).
|
||||
*
|
||||
* Only call this while you know it will remove the link too.
|
||||
*/
|
||||
void RE_FreeRender(struct Render *re);
|
||||
void RE_FreeViewRender(struct ViewRender *view_render);
|
||||
/**
|
||||
* Only called on exit.
|
||||
*/
|
||||
void RE_FreeAllRender();
|
||||
|
||||
/**
|
||||
* On file load, free all interactive compositor renders.
|
||||
*/
|
||||
void RE_FreeInteractiveCompositorRenders();
|
||||
|
||||
/**
|
||||
* On file load, free render results.
|
||||
*/
|
||||
void RE_FreeAllRenderResults();
|
||||
|
||||
/**
|
||||
* On file load or changes engines, free persistent render data.
|
||||
* Assumes no engines are currently rendering.
|
||||
*/
|
||||
void RE_FreeAllPersistentData();
|
||||
/**
|
||||
* Free persistent render data, optionally only for the given scene.
|
||||
*/
|
||||
void RE_FreePersistentData(const struct Scene *scene);
|
||||
|
||||
/**
|
||||
* Free cached GPU textures to reduce memory usage.
|
||||
*/
|
||||
void RE_FreeGPUTextureCaches();
|
||||
|
||||
/**
|
||||
* Free cached GPU textures, contexts and compositor to reduce memory usage,
|
||||
* when nothing in the UI requires them anymore.
|
||||
*/
|
||||
void RE_FreeUnusedGPUResources();
|
||||
|
||||
/**
|
||||
* Get results and statistics.
|
||||
*/
|
||||
void RE_FreeRenderResult(struct RenderResult *rr);
|
||||
/**
|
||||
* If you want to know exactly what has been done.
|
||||
*/
|
||||
struct RenderResult *RE_AcquireResultRead(struct Render *re);
|
||||
struct RenderResult *RE_AcquireResultWrite(struct Render *re);
|
||||
void RE_ReferenceRenderResult(struct RenderResult *rr);
|
||||
void RE_ReleaseResult(struct Render *re);
|
||||
/**
|
||||
* Same as #RE_AcquireResultImage but creating the necessary views to store the result
|
||||
* fill provided result struct with a copy of thew views of what is done so far the
|
||||
* #RenderResult.views #ListBaseT needs to be freed after with #RE_ReleaseResultImageViews
|
||||
*/
|
||||
void RE_AcquireResultImageViews(struct Render *re, struct RenderResult *rr);
|
||||
/**
|
||||
* Clear temporary #RenderResult struct.
|
||||
*/
|
||||
void RE_ReleaseResultImageViews(struct Render *re, struct RenderResult *rr);
|
||||
|
||||
/**
|
||||
* Fill provided result struct with what's currently active or done.
|
||||
* This #RenderResult struct is the only exception to the rule of a #RenderResult
|
||||
* always having at least one #RenderView.
|
||||
*/
|
||||
void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, int view_id);
|
||||
void RE_ReleaseResultImage(struct Render *re);
|
||||
void RE_SwapResult(struct Render *re, struct RenderResult **rr);
|
||||
void RE_ClearResult(struct Render *re);
|
||||
struct RenderStats *RE_GetStats(struct Render *re);
|
||||
|
||||
/**
|
||||
* Caller is responsible for allocating `dst` in correct size!
|
||||
*/
|
||||
void RE_ResultGet32(Render *re, uint8_t *dst);
|
||||
|
||||
bool RE_ResultIsMultiView(struct RenderResult *rr);
|
||||
|
||||
void RE_render_result_full_channel_name(char *fullname,
|
||||
const char *layname,
|
||||
const char *passname,
|
||||
const char *viewname,
|
||||
const char *chan_id,
|
||||
int channel);
|
||||
|
||||
struct ImBuf *RE_render_result_rect_to_ibuf(struct RenderResult *rr,
|
||||
const struct ImageFormatData *imf,
|
||||
const float dither,
|
||||
int view_id);
|
||||
void RE_render_result_rect_from_ibuf(struct RenderResult *rr,
|
||||
const struct ImBuf *ibuf,
|
||||
int view_id);
|
||||
|
||||
struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name);
|
||||
float *RE_RenderLayerGetPass(struct RenderLayer *rl, const char *name, const char *viewname);
|
||||
struct ImBuf *RE_RenderLayerGetPassImBuf(struct RenderLayer *rl,
|
||||
const char *name,
|
||||
const char *viewname);
|
||||
|
||||
bool RE_HasSingleLayer(struct Render *re);
|
||||
|
||||
/**
|
||||
* Add passes for grease pencil.
|
||||
* Create a render-layer and render-pass for grease-pencil layer.
|
||||
*/
|
||||
struct RenderPass *RE_create_gp_pass(struct RenderResult *rr,
|
||||
const char *layername,
|
||||
const char *viewname);
|
||||
|
||||
void RE_create_render_pass(struct RenderResult *rr,
|
||||
const char *name,
|
||||
int channels,
|
||||
const char *chan_id,
|
||||
const char *layername,
|
||||
const char *viewname,
|
||||
bool allocate);
|
||||
|
||||
/**
|
||||
* Obligatory initialize call, doesn't change during entire render sequence.
|
||||
* \param disprect: is optional. if NULL it assumes full window render.
|
||||
*/
|
||||
void RE_InitState(struct Render *re,
|
||||
struct Render *source,
|
||||
struct RenderData *rd,
|
||||
ListBaseT<ViewLayer> *render_layers,
|
||||
struct ViewLayer *single_layer,
|
||||
int winx,
|
||||
int winy,
|
||||
const rcti *disprect);
|
||||
|
||||
/**
|
||||
* Set up the view-plane/perspective matrix, three choices.
|
||||
*
|
||||
* \return camera override if set.
|
||||
*/
|
||||
struct Object *RE_GetCamera(struct Render *re);
|
||||
void RE_SetOverrideCamera(struct Render *re, struct Object *cam_ob);
|
||||
/**
|
||||
* Per render, there's one persistent view-plane. Parts will set their own view-planes.
|
||||
*
|
||||
* \note call this after #RE_InitState().
|
||||
*/
|
||||
void RE_SetCamera(struct Render *re, const struct Object *cam_ob);
|
||||
|
||||
/**
|
||||
* Get current view and window transform.
|
||||
*/
|
||||
void RE_GetViewPlane(struct Render *re, rctf *r_viewplane, rcti *r_disprect);
|
||||
|
||||
/**
|
||||
* Set the render threads based on the command-line and auto-threads setting.
|
||||
*/
|
||||
void RE_init_threadcount(Render *re);
|
||||
|
||||
bool RE_WriteRenderViewsMovie(struct ReportList *reports,
|
||||
struct RenderResult *rr,
|
||||
struct Scene *scene,
|
||||
struct RenderData *rd,
|
||||
struct MovieWriter **movie_writers,
|
||||
int totvideos,
|
||||
bool preview);
|
||||
|
||||
/**
|
||||
* General Blender frame render call.
|
||||
*
|
||||
* \note Only #RE_NewRender() needed, main Blender render calls.
|
||||
*
|
||||
* \param write_still: Saves frames to disk (typically disabled). Useful for batch-operations
|
||||
* (e.g. rendering from Python) when an additional save action for is inconvenient.
|
||||
* This is the default behavior for #RE_RenderAnim.
|
||||
*/
|
||||
void RE_RenderFrame(struct Render *re,
|
||||
struct Main *bmain,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *single_layer,
|
||||
struct Object *camera_override,
|
||||
int frame,
|
||||
float subframe,
|
||||
bool write_still);
|
||||
/**
|
||||
* A version of #RE_RenderFrame that saves images to disk.
|
||||
*/
|
||||
void RE_RenderAnim(struct Render *re,
|
||||
struct Main *bmain,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *single_layer,
|
||||
struct Object *camera_override,
|
||||
int sfra,
|
||||
int efra,
|
||||
int tfra);
|
||||
#ifdef WITH_FREESTYLE
|
||||
void RE_RenderFreestyleStrokes(struct Render *re,
|
||||
struct Main *bmain,
|
||||
struct Scene *scene,
|
||||
bool render);
|
||||
void RE_RenderFreestyleExternal(struct Render *re);
|
||||
#endif
|
||||
|
||||
void RE_SetActiveRenderView(struct Render *re, const char *viewname);
|
||||
const char *RE_GetActiveRenderView(struct Render *re);
|
||||
|
||||
/**
|
||||
* Error reporting.
|
||||
*/
|
||||
void RE_SetReports(struct Render *re, struct ReportList *reports);
|
||||
|
||||
/**
|
||||
* Main preview render call.
|
||||
*/
|
||||
void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene);
|
||||
|
||||
/**
|
||||
* Only the temp file!
|
||||
*/
|
||||
bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode);
|
||||
|
||||
struct RenderResult *RE_MultilayerConvert(
|
||||
ExrHandle *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
|
||||
/**
|
||||
* Display, event callbacks and GPU contexts
|
||||
* */
|
||||
|
||||
void RE_display_init(Render *re);
|
||||
void RE_display_ensure_gpu_context(Render *re);
|
||||
void RE_display_share(Render *re, const Render *parent_re);
|
||||
void RE_display_free(Render *re);
|
||||
|
||||
void RE_display_update_cb(struct Render *re,
|
||||
void *handle,
|
||||
void (*f)(void *handle, RenderResult *rr, struct rcti *rect));
|
||||
void RE_stats_draw_cb(struct Render *re, void *handle, void (*f)(void *handle, RenderStats *rs));
|
||||
void RE_progress_cb(struct Render *re, void *handle, void (*f)(void *handle, float));
|
||||
void RE_draw_lock_cb(struct Render *re, void *handle, void (*f)(void *handle, bool lock));
|
||||
void RE_test_break_cb(struct Render *re, void *handle, bool (*f)(void *handle));
|
||||
void RE_prepare_viewlayer_cb(struct Render *re,
|
||||
void *handle,
|
||||
bool (*f)(void *handle, ViewLayer *vl, struct Depsgraph *depsgraph));
|
||||
void RE_current_scene_update_cb(struct Render *re,
|
||||
void *handle,
|
||||
void (*f)(void *handle, struct Scene *scene));
|
||||
|
||||
GHOST_IContext *RE_system_gpu_context_get(Render *re);
|
||||
void *RE_blender_gpu_context_ensure(Render *re);
|
||||
|
||||
bool RE_seq_render_active(struct Scene *scene, struct RenderData *rd);
|
||||
|
||||
/**
|
||||
* Used in the interface to decide whether to show layers or passes.
|
||||
*/
|
||||
bool RE_layers_have_name(struct RenderResult *result);
|
||||
bool RE_passes_have_name(struct RenderLayer *rl);
|
||||
|
||||
struct RenderPass *RE_pass_find_by_name(struct RenderLayer *rl,
|
||||
const char *name,
|
||||
const char *viewname);
|
||||
|
||||
/**
|
||||
* Set the buffer data of the render pass.
|
||||
* The pass takes ownership of the data, and creates an implicit sharing handle to allow its
|
||||
* sharing with other users.
|
||||
*/
|
||||
void RE_pass_set_buffer_data(struct RenderPass *pass, float *data);
|
||||
|
||||
/**
|
||||
* Ensure a GPU texture corresponding to the render buffer data exists.
|
||||
*/
|
||||
gpu::Texture *RE_pass_ensure_gpu_texture_cache(struct Render *re, struct RenderPass *rpass);
|
||||
|
||||
void RE_GetCameraWindow(struct Render *re, const struct Object *camera, float r_winmat[4][4]);
|
||||
/**
|
||||
* Must be called after #RE_GetCameraWindow(), does not change `re->winmat`.
|
||||
*/
|
||||
void RE_GetCameraWindowWithOverscan(const struct Render *re, float overscan, float r_winmat[4][4]);
|
||||
void RE_GetCameraModelMatrix(const struct Render *re,
|
||||
const struct Object *camera,
|
||||
float r_modelmat[4][4]);
|
||||
|
||||
void RE_GetWindowMatrixWithOverscan(bool is_ortho,
|
||||
float clip_start,
|
||||
float clip_end,
|
||||
rctf viewplane,
|
||||
float overscan,
|
||||
float r_winmat[4][4]);
|
||||
|
||||
struct Scene *RE_GetScene(struct Render *re);
|
||||
void RE_SetScene(struct Render *re, struct Scene *sce);
|
||||
|
||||
/* When rendering an animation, saving files is required, either through scene saving or through
|
||||
* a compositor File Output node. */
|
||||
bool RE_disable_save_output_allowed(const bool is_animation, Scene &scene, ReportList *reports);
|
||||
|
||||
bool RE_is_rendering_allowed(const Main &bmain,
|
||||
struct Scene *scene,
|
||||
struct ViewLayer *single_layer,
|
||||
struct Object *camera_override,
|
||||
struct ReportList *reports);
|
||||
|
||||
bool RE_allow_render_generic_object(struct Object *ob);
|
||||
|
||||
/******* defined in `render_result.cc` *********/
|
||||
|
||||
bool RE_HasCombinedLayer(const RenderResult *result);
|
||||
bool RE_HasFloatPixels(const RenderResult *result);
|
||||
bool RE_RenderResult_is_stereo(const RenderResult *result);
|
||||
struct RenderView *RE_RenderViewGetById(struct RenderResult *rr, int view_id);
|
||||
struct RenderView *RE_RenderViewGetByName(struct RenderResult *rr, const char *viewname);
|
||||
|
||||
RenderResult *RE_DuplicateRenderResult(RenderResult *rr);
|
||||
|
||||
struct ImBuf *RE_RenderPassEnsureImBuf(RenderPass *render_pass);
|
||||
struct ImBuf *RE_RenderViewEnsureImBuf(const RenderResult *render_result, RenderView *render_view);
|
||||
|
||||
/* Returns true if the pass is a color (as opposite of data) and needs to be color managed. */
|
||||
bool RE_RenderPassIsColor(const RenderPass *render_pass);
|
||||
|
||||
} // namespace blender
|
||||
107
blender-5.2.0/source/blender/render/RE_texture.h
Normal file
107
blender-5.2.0/source/blender/render/RE_texture.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*
|
||||
* This include is for non-render pipeline exports (still old cruft here).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
/* called by meshtools */
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct ImagePool;
|
||||
struct MTex;
|
||||
struct Tex;
|
||||
|
||||
/* `texture_procedural.cc` */
|
||||
|
||||
/**
|
||||
* \param pool: Thread pool, may be NULL.
|
||||
*
|
||||
* \return True if the texture has color, otherwise false.
|
||||
*/
|
||||
bool RE_texture_evaluate(const struct MTex *mtex,
|
||||
const float vec[3],
|
||||
int thread,
|
||||
struct ImagePool *pool,
|
||||
bool skip_load_image,
|
||||
bool texnode_preview,
|
||||
/* Return arguments. */
|
||||
float *r_intensity,
|
||||
float r_rgba[4]) ATTR_NONNULL(1, 2, 7, 8);
|
||||
|
||||
/**
|
||||
* \param tex: Texture.
|
||||
* \param out: Previous color.
|
||||
* \param fact: Texture strength.
|
||||
* \param facg: Button strength value.
|
||||
*/
|
||||
float texture_value_blend(float tex, float out, float fact, float facg, int blendtype);
|
||||
|
||||
void RE_texture_rng_init();
|
||||
void RE_texture_rng_exit();
|
||||
|
||||
/* `texture_procedural.cc` */
|
||||
|
||||
/**
|
||||
* Texture evaluation result.
|
||||
*/
|
||||
struct TexResult {
|
||||
float tin;
|
||||
float trgba[4];
|
||||
/* Is actually a boolean: When true -> use alpha, false -> set alpha to 1.0. */
|
||||
int talpha;
|
||||
};
|
||||
|
||||
/* This one uses nodes. */
|
||||
|
||||
/**
|
||||
* WARNING(@ideasman42): if the texres's values are not declared zero,
|
||||
* check the return value to be sure the color values are set before using the r/g/b values,
|
||||
* otherwise you may use uninitialized values.
|
||||
*
|
||||
* Use it for stuff which is out of render pipeline.
|
||||
*/
|
||||
int multitex_ext(struct Tex *tex,
|
||||
const float texvec[3],
|
||||
struct TexResult *texres,
|
||||
short thread,
|
||||
struct ImagePool *pool,
|
||||
bool scene_color_manage,
|
||||
bool skip_load_image);
|
||||
|
||||
/**
|
||||
* Nodes disabled.
|
||||
* extern-tex doesn't support nodes (#ntreeBeginExec() can't be called when rendering is going on).
|
||||
*
|
||||
* Use it for stuff which is out of render pipeline.
|
||||
*/
|
||||
int multitex_ext_safe(struct Tex *tex,
|
||||
const float texvec[3],
|
||||
struct TexResult *texres,
|
||||
struct ImagePool *pool,
|
||||
bool scene_color_manage,
|
||||
bool skip_load_image);
|
||||
|
||||
/**
|
||||
* Only for internal node usage.
|
||||
*
|
||||
* this is called from the shader and texture nodes
|
||||
* Use it from render pipeline only!
|
||||
*/
|
||||
int multitex_nodes(struct Tex *tex,
|
||||
const float texvec[3],
|
||||
struct TexResult *texres,
|
||||
short thread,
|
||||
short which_output,
|
||||
const struct MTex *mtex,
|
||||
struct ImagePool *pool);
|
||||
|
||||
} // namespace blender
|
||||
33
blender-5.2.0/source/blender/render/RE_texture_margin.h
Normal file
33
blender-5.2.0/source/blender/render/RE_texture_margin.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ImBuf;
|
||||
struct Mesh;
|
||||
|
||||
/**
|
||||
* Generate a margin around the textures uv islands by copying pixels from the adjacent polygon.
|
||||
*
|
||||
* \param ibuf: the texture image.
|
||||
* \param mask: pixels with a mask value of 1 are not written to.
|
||||
* \param margin: the size of the margin in pixels.
|
||||
* \param me: the mesh to use the polygons of.
|
||||
* \param uv_layer: The UV layer to use.
|
||||
*/
|
||||
void RE_generate_texturemargin_adjacentfaces(struct ImBuf *ibuf,
|
||||
char *mask,
|
||||
int margin,
|
||||
struct Mesh const *me,
|
||||
StringRef uv_layer,
|
||||
const float uv_offset[2]);
|
||||
|
||||
} // namespace blender
|
||||
104
blender-5.2.0/source/blender/render/hydra/CMakeLists.txt
Normal file
104
blender-5.2.0/source/blender/render/hydra/CMakeLists.txt
Normal file
@@ -0,0 +1,104 @@
|
||||
# SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# This suppresses the warning "This file includes at least one deprecated or antiquated
|
||||
# header which may be removed without further notice at a future date", which is caused
|
||||
# by the USD library including <ext/hash_set> on Linux. This has been reported at:
|
||||
# https://github.com/PixarAnimationStudios/USD/issues/1057.
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_definitions(-D_GLIBCXX_PERMIT_BACKWARD_HASH)
|
||||
endif()
|
||||
if(WIN32)
|
||||
# We need to keep BOOST_DEBUG_PYTHON for the vendored copy of boost inside USD.
|
||||
add_definitions(-DBOOST_DEBUG_PYTHON)
|
||||
endif()
|
||||
|
||||
# Pre-compiled Linux libraries are made with GCC, and USD uses some extensions
|
||||
# which lead to an incompatible ABI for Clang. Using those extensions with
|
||||
# Clang as well works around the issue.
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
if(DEFINED LIBDIR)
|
||||
add_definitions(-DARCH_HAS_GNU_STL_EXTENSIONS)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# USD headers use deprecated TBB headers, silence warning.
|
||||
add_definitions(-DTBB_SUPPRESS_DEPRECATED_MESSAGES=1)
|
||||
|
||||
if(WIN32)
|
||||
# Some USD library headers trigger the "unreferenced formal parameter"
|
||||
# warning alert.
|
||||
# Silence them by restore warn C4100 back to w4
|
||||
remove_c_and_cxx_flag("/w34100")
|
||||
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
||||
# USD currently does not support the new preprocessor,
|
||||
# so we remove it here and disable sse2neon
|
||||
remove_c_and_cxx_flag("/Zc:preprocessor")
|
||||
add_definitions(-DDISABLE_SSE2NEON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
add_definitions(-DWITH_OPENGL_BACKEND)
|
||||
endif()
|
||||
|
||||
set(INC
|
||||
../../../../intern/guardedalloc
|
||||
../../blenlib
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../io/usd
|
||||
../../gpu/intern
|
||||
../../python/intern
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
..
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::clog
|
||||
bf_io_usd
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
PRIVATE bf::dependencies::optional::usd
|
||||
PRIVATE bf::dependencies::optional::tbb
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
PRIVATE bf::dependencies::gflags
|
||||
PRIVATE bf::dependencies::eigen
|
||||
)
|
||||
|
||||
set(SRC
|
||||
camera.cc
|
||||
engine.cc
|
||||
final_engine.cc
|
||||
light_tasks_delegate.cc
|
||||
preview_engine.cc
|
||||
python.cc
|
||||
render_task_delegate.cc
|
||||
viewport_engine.cc
|
||||
|
||||
camera.hh
|
||||
engine.hh
|
||||
final_engine.hh
|
||||
light_tasks_delegate.hh
|
||||
preview_engine.hh
|
||||
render_task_delegate.hh
|
||||
settings.hh
|
||||
viewport_engine.hh
|
||||
)
|
||||
|
||||
blender_add_lib(bf_render_hydra "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_render_hydra bf_rna)
|
||||
137
blender-5.2.0/source/blender/render/hydra/camera.cc
Normal file
137
blender-5.2.0/source/blender/render/hydra/camera.cc
Normal file
@@ -0,0 +1,137 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "camera.hh"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
static pxr::GfMatrix4d gf_matrix_from_transform(const float m[4][4])
|
||||
{
|
||||
pxr::GfMatrix4d ret;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
ret[i][j] = m[i][j];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gf_camera_fill_dof_data(const Object *camera_obj, pxr::GfCamera *gf_camera)
|
||||
{
|
||||
if (camera_obj == nullptr || camera_obj->type != OB_CAMERA) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Camera *camera = id_cast<Camera *>(camera_obj->data);
|
||||
if (!(camera->dof.flag & CAM_DOF_ENABLED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* World units. Handles DoF object and value. Object takes precedence. */
|
||||
const float focus_distance = BKE_camera_object_dof_distance(camera_obj);
|
||||
gf_camera->SetFocusDistance(focus_distance);
|
||||
|
||||
/*
|
||||
* F-stop is unit-less, however it's a ratio between focal length and aperture diameter.
|
||||
* The aperture must be in the same unit for correctness.
|
||||
* Focal length in GfCamera is defined in tenths of a world unit.
|
||||
*
|
||||
* Following the logic of USD camera data writer:
|
||||
* tenth_unit_to_meters = 1 / 10
|
||||
* tenth_unit_to_millimeters = 1000 * tenth_unit_to_meters = 100
|
||||
* Scene's units scale is not used for camera's focal length.
|
||||
*/
|
||||
gf_camera->SetFStop(camera->dof.aperture_fstop * 100.0);
|
||||
}
|
||||
|
||||
static pxr::GfCamera gf_camera(const CameraParams ¶ms,
|
||||
const pxr::GfVec2i &res,
|
||||
const pxr::GfVec4f &border)
|
||||
{
|
||||
pxr::GfCamera camera;
|
||||
|
||||
camera.SetProjection(params.is_ortho ? pxr::GfCamera::Projection::Orthographic :
|
||||
pxr::GfCamera::Projection::Perspective);
|
||||
camera.SetClippingRange(pxr::GfRange1f(params.clip_start, params.clip_end));
|
||||
camera.SetFocalLength(params.lens);
|
||||
|
||||
pxr::GfVec2f b_pos(border[0], border[1]), b_size(border[2], border[3]);
|
||||
float sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y);
|
||||
pxr::GfVec2f sensor_scale = (BKE_camera_sensor_fit(params.sensor_fit, res[0], res[1]) ==
|
||||
CAMERA_SENSOR_FIT_HOR) ?
|
||||
pxr::GfVec2f(1.0f, float(res[1]) / res[0]) :
|
||||
pxr::GfVec2f(float(res[0]) / res[1], 1.0f);
|
||||
pxr::GfVec2f aperture = pxr::GfVec2f((params.is_ortho) ? params.ortho_scale : sensor_size);
|
||||
aperture = pxr::GfCompMult(aperture, sensor_scale);
|
||||
aperture = pxr::GfCompMult(aperture, b_size);
|
||||
aperture *= params.zoom;
|
||||
if (params.is_ortho) {
|
||||
/* Use tenths of a world unit according to USD docs
|
||||
* https://graphics.pixar.com/usd/docs/api/class_gf_camera.html */
|
||||
aperture *= 10.0f;
|
||||
}
|
||||
camera.SetHorizontalAperture(aperture[0]);
|
||||
camera.SetVerticalAperture(aperture[1]);
|
||||
|
||||
pxr::GfVec2f lens_shift = pxr::GfVec2f(params.shiftx, params.shifty);
|
||||
lens_shift = pxr::GfCompDiv(lens_shift, sensor_scale);
|
||||
lens_shift += pxr::GfVec2f(params.offsetx, params.offsety);
|
||||
lens_shift += b_pos + b_size * 0.5f - pxr::GfVec2f(0.5f);
|
||||
lens_shift = pxr::GfCompDiv(lens_shift, b_size);
|
||||
camera.SetHorizontalApertureOffset(lens_shift[0] * aperture[0]);
|
||||
camera.SetVerticalApertureOffset(lens_shift[1] * aperture[1]);
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
pxr::GfCamera gf_camera(const Depsgraph *depsgraph,
|
||||
const View3D *v3d,
|
||||
const ARegion *region,
|
||||
const pxr::GfVec4f &border)
|
||||
{
|
||||
const RegionView3D *region_data = static_cast<const RegionView3D *>(region->regiondata);
|
||||
const Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
|
||||
CameraParams params;
|
||||
BKE_camera_params_init(¶ms);
|
||||
BKE_camera_params_from_view3d(¶ms, depsgraph, v3d, region_data);
|
||||
|
||||
pxr::GfCamera camera = gf_camera(params, pxr::GfVec2i(region->winx, region->winy), border);
|
||||
camera.SetTransform(gf_matrix_from_transform(region_data->viewmat).GetInverse());
|
||||
|
||||
/* Ensure viewport is in active camera view mode. */
|
||||
if (region_data->persp == RV3D_CAMOB) {
|
||||
gf_camera_fill_dof_data(scene->camera, &camera);
|
||||
}
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
pxr::GfCamera gf_camera(const Object *camera_obj,
|
||||
const pxr::GfVec2i &res,
|
||||
const pxr::GfVec4f &border)
|
||||
{
|
||||
CameraParams params;
|
||||
BKE_camera_params_init(¶ms);
|
||||
BKE_camera_params_from_object(¶ms, camera_obj);
|
||||
|
||||
pxr::GfCamera camera = gf_camera(params, res, border);
|
||||
camera.SetTransform(gf_matrix_from_transform(camera_obj->object_to_world().ptr()));
|
||||
|
||||
gf_camera_fill_dof_data(camera_obj, &camera);
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
28
blender-5.2.0/source/blender/render/hydra/camera.hh
Normal file
28
blender-5.2.0/source/blender/render/hydra/camera.hh
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/base/gf/camera.h>
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegion;
|
||||
struct Depsgraph;
|
||||
struct Object;
|
||||
struct View3D;
|
||||
namespace render::hydra {
|
||||
|
||||
pxr::GfCamera gf_camera(const Depsgraph *depsgraph,
|
||||
const View3D *v3d,
|
||||
const ARegion *region,
|
||||
const pxr::GfVec4f &border);
|
||||
|
||||
pxr::GfCamera gf_camera(const Object *camera_obj,
|
||||
const pxr::GfVec2i &res,
|
||||
const pxr::GfVec4f &border);
|
||||
|
||||
} // namespace render::hydra
|
||||
} // namespace blender
|
||||
180
blender-5.2.0/source/blender/render/hydra/engine.cc
Normal file
180
blender-5.2.0/source/blender/render/hydra/engine.cc
Normal file
@@ -0,0 +1,180 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "engine.hh"
|
||||
|
||||
#include <pxr/base/plug/plugin.h>
|
||||
#include <pxr/base/plug/registry.h>
|
||||
#include <pxr/imaging/hd/flatteningSceneIndex.h>
|
||||
#include <pxr/imaging/hd/rendererPluginRegistry.h>
|
||||
#include <pxr/imaging/hdSt/renderDelegate.h>
|
||||
#include <pxr/imaging/hdsi/extComputationPrimvarPruningSceneIndex.h>
|
||||
#include <pxr/imaging/hgi/tokens.h>
|
||||
#include <pxr/usd/usdGeom/tokens.h>
|
||||
#include <pxr/usdImaging/usdImaging/flattenedDataSourceProviders.h>
|
||||
#include <pxr/usdImaging/usdImaging/materialBindingsResolvingSceneIndex.h>
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
CLG_LOGREF_DECLARE_GLOBAL(LOG_HYDRA_RENDER, "hydra.render");
|
||||
|
||||
Engine::Engine(RenderEngine *bl_engine, const std::string &render_delegate_name)
|
||||
: render_delegate_name_(render_delegate_name), bl_engine_(bl_engine)
|
||||
{
|
||||
pxr::HdRendererPluginRegistry ®istry = pxr::HdRendererPluginRegistry::GetInstance();
|
||||
|
||||
pxr::TF_PY_ALLOW_THREADS_IN_SCOPE();
|
||||
|
||||
pxr::HdDriverVector hd_drivers;
|
||||
if (bl_engine->type->flag & RE_USE_GPU_CONTEXT) {
|
||||
pxr::TfToken hgi_token;
|
||||
switch (GPU_backend_get_type()) {
|
||||
case GPU_BACKEND_METAL:
|
||||
hgi_token = pxr::TfToken("Metal");
|
||||
break;
|
||||
case GPU_BACKEND_OPENGL:
|
||||
hgi_token = pxr::TfToken("OpenGL");
|
||||
break;
|
||||
case GPU_BACKEND_VULKAN:
|
||||
hgi_token = pxr::TfToken("Vulkan");
|
||||
break;
|
||||
case GPU_BACKEND_NONE:
|
||||
case GPU_BACKEND_ANY:
|
||||
/* When pxr::Hgi::CreateNamedHgi is called with an empty token it will select the default
|
||||
* platform Hgi. */
|
||||
break;
|
||||
}
|
||||
hgi_ = pxr::Hgi::CreateNamedHgi(hgi_token);
|
||||
hgi_driver_.name = pxr::HgiTokens->renderDriver;
|
||||
hgi_driver_.driver = pxr::VtValue(hgi_.get());
|
||||
|
||||
hd_drivers.push_back(&hgi_driver_);
|
||||
}
|
||||
render_delegate_ = registry.CreateRenderDelegate(pxr::TfToken(render_delegate_name_));
|
||||
|
||||
if (!render_delegate_) {
|
||||
throw std::runtime_error("Cannot create render delegate: " + render_delegate_name_);
|
||||
}
|
||||
|
||||
render_index_.reset(pxr::HdRenderIndex::New(render_delegate_.Get(), hd_drivers));
|
||||
free_camera_delegate_ = std::make_unique<io::hydra::CameraDelegate>(
|
||||
render_index_.get(), pxr::SdfPath::AbsoluteRootPath().AppendElementString("freeCamera"));
|
||||
|
||||
/* Tasks are exposed as task prims through a retained scene index. */
|
||||
const bool needs_prefixing = false;
|
||||
task_scene_index_ = pxr::HdRetainedSceneIndex::New();
|
||||
render_index_->InsertSceneIndex(
|
||||
task_scene_index_, pxr::SdfPath::AbsoluteRootPath(), needs_prefixing);
|
||||
|
||||
if (bl_engine->type->flag & RE_USE_GPU_CONTEXT && GPU_backend_get_type() == GPU_BACKEND_OPENGL) {
|
||||
render_task_delegate_ = std::make_unique<GPURenderTaskDelegate>(
|
||||
render_index_.get(),
|
||||
task_scene_index_,
|
||||
pxr::SdfPath::AbsoluteRootPath().AppendElementString("renderTask"));
|
||||
}
|
||||
else {
|
||||
render_task_delegate_ = std::make_unique<RenderTaskDelegate>(
|
||||
render_index_.get(),
|
||||
task_scene_index_,
|
||||
pxr::SdfPath::AbsoluteRootPath().AppendElementString("renderTask"));
|
||||
}
|
||||
render_task_delegate_->set_camera(free_camera_delegate_->GetCameraId());
|
||||
|
||||
if (render_delegate_name_ == "HdStormRendererPlugin") {
|
||||
light_tasks_delegate_ = std::make_unique<LightTasksDelegate>(
|
||||
render_index_.get(),
|
||||
task_scene_index_,
|
||||
pxr::SdfPath::AbsoluteRootPath().AppendElementString("lightTasks"));
|
||||
light_tasks_delegate_->set_camera(free_camera_delegate_->GetCameraId());
|
||||
}
|
||||
|
||||
engine_ = std::make_unique<pxr::HdEngine>();
|
||||
}
|
||||
|
||||
void Engine::sync(Depsgraph *depsgraph, bContext *context)
|
||||
{
|
||||
depsgraph_ = depsgraph;
|
||||
context_ = context;
|
||||
scene_ = DEG_get_evaluated_scene(depsgraph);
|
||||
|
||||
const bool use_materialx = bl_engine_->type->flag & RE_USE_MATERIALX;
|
||||
|
||||
if (scene_->hydra.export_method == SCE_HYDRA_EXPORT_HYDRA) {
|
||||
/* Fast path. */
|
||||
usd_scene_delegate_.reset();
|
||||
|
||||
if (!hydra_scene_index_) {
|
||||
hydra_scene_index_path_ = pxr::SdfPath::AbsoluteRootPath().AppendElementString("scene");
|
||||
hydra_scene_index_ = std::make_unique<io::hydra::HydraSceneIndex>(
|
||||
hydra_scene_index_path_, render_delegate_.Get(), use_materialx);
|
||||
const bool needs_prefixing = false;
|
||||
pxr::HdSceneIndexBaseRefPtr filtered = hydra_scene_index_->retained();
|
||||
filtered = pxr::HdSiExtComputationPrimvarPruningSceneIndex::New(filtered);
|
||||
filtered = pxr::HdFlatteningSceneIndex::New(filtered,
|
||||
pxr::UsdImagingFlattenedDataSourceProviders());
|
||||
filtered = pxr::UsdImagingMaterialBindingsResolvingSceneIndex::New(filtered, nullptr);
|
||||
render_index_->InsertSceneIndex(filtered, hydra_scene_index_path_, needs_prefixing);
|
||||
}
|
||||
hydra_scene_index_->populate(depsgraph, context ? CTX_wm_view3d(context) : nullptr);
|
||||
}
|
||||
else {
|
||||
/* Slow USD export for reference. */
|
||||
if (hydra_scene_index_) {
|
||||
hydra_scene_index_->clear();
|
||||
}
|
||||
|
||||
if (!usd_scene_delegate_) {
|
||||
pxr::SdfPath scene_path = pxr::SdfPath::AbsoluteRootPath().AppendElementString("usd_scene");
|
||||
usd_scene_delegate_ = std::make_unique<io::hydra::USDSceneIndex>(
|
||||
render_index_.get(), scene_path, use_materialx);
|
||||
}
|
||||
usd_scene_delegate_->populate(depsgraph);
|
||||
}
|
||||
free_camera_delegate_->sync(scene_);
|
||||
}
|
||||
|
||||
void Engine::set_render_setting(const std::string &key, const pxr::VtValue &val)
|
||||
{
|
||||
render_delegate_->SetRenderSetting(pxr::TfToken(key), val);
|
||||
}
|
||||
|
||||
float Engine::renderer_percent_done()
|
||||
{
|
||||
pxr::VtDictionary render_stats = render_delegate_->GetRenderStats();
|
||||
auto it = render_stats.find("percentDone");
|
||||
if (it == render_stats.end()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return float(it->second.UncheckedGet<double>());
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtrVector Engine::tasks()
|
||||
{
|
||||
pxr::HdTaskSharedPtrVector res;
|
||||
if (light_tasks_delegate_) {
|
||||
if (scene_->r.alphamode != R_ALPHAPREMUL) {
|
||||
#ifndef __APPLE__
|
||||
/* TODO: Temporary disable skydome task for MacOS due to crash with error:
|
||||
* Failed to create pipeline state, error depthAttachmentPixelFormat is not valid
|
||||
* and shader writes to depth */
|
||||
res.push_back(light_tasks_delegate_->skydome_task());
|
||||
#endif
|
||||
}
|
||||
res.push_back(light_tasks_delegate_->simple_task());
|
||||
}
|
||||
res.push_back(render_task_delegate_->task());
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
79
blender-5.2.0/source/blender/render/hydra/engine.hh
Normal file
79
blender-5.2.0/source/blender/render/hydra/engine.hh
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <pxr/imaging/hd/driver.h>
|
||||
#include <pxr/imaging/hd/engine.h>
|
||||
#include <pxr/imaging/hd/pluginRenderDelegateUniqueHandle.h>
|
||||
#include <pxr/imaging/hd/retainedSceneIndex.h>
|
||||
#include <pxr/imaging/hgi/hgi.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usdImaging/usdImaging/delegate.h>
|
||||
|
||||
#include "hydra/camera_delegate.hh"
|
||||
#include "hydra/scene_index.hh"
|
||||
#include "hydra/settings.hh"
|
||||
#include "hydra/usd_scene_index.hh"
|
||||
|
||||
#include "light_tasks_delegate.hh"
|
||||
#include "render_task_delegate.hh"
|
||||
|
||||
struct CLG_LogRef;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct RenderEngine;
|
||||
|
||||
namespace render::hydra {
|
||||
|
||||
extern CLG_LogRef *LOG_HYDRA_RENDER;
|
||||
|
||||
class Engine {
|
||||
protected:
|
||||
std::string render_delegate_name_;
|
||||
RenderEngine *bl_engine_ = nullptr;
|
||||
Depsgraph *depsgraph_ = nullptr;
|
||||
bContext *context_ = nullptr;
|
||||
Scene *scene_ = nullptr;
|
||||
|
||||
/* The order is important due to deletion order */
|
||||
pxr::HgiUniquePtr hgi_;
|
||||
pxr::HdDriver hgi_driver_;
|
||||
pxr::HdPluginRenderDelegateUniqueHandle render_delegate_;
|
||||
std::unique_ptr<pxr::HdRenderIndex> render_index_;
|
||||
|
||||
std::unique_ptr<io::hydra::CameraDelegate> free_camera_delegate_;
|
||||
std::unique_ptr<io::hydra::HydraSceneIndex> hydra_scene_index_;
|
||||
pxr::SdfPath hydra_scene_index_path_;
|
||||
std::unique_ptr<io::hydra::USDSceneIndex> usd_scene_delegate_;
|
||||
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index_;
|
||||
std::unique_ptr<RenderTaskDelegate> render_task_delegate_;
|
||||
std::unique_ptr<LightTasksDelegate> light_tasks_delegate_;
|
||||
std::unique_ptr<pxr::HdEngine> engine_;
|
||||
|
||||
public:
|
||||
Engine(RenderEngine *bl_engine, const std::string &render_delegate_name);
|
||||
virtual ~Engine() = default;
|
||||
|
||||
void sync(Depsgraph *depsgraph, bContext *context);
|
||||
virtual void render() = 0;
|
||||
|
||||
virtual void set_render_setting(const std::string &key, const pxr::VtValue &val);
|
||||
|
||||
protected:
|
||||
float renderer_percent_done();
|
||||
pxr::HdTaskSharedPtrVector tasks();
|
||||
virtual void notify_status(float progress,
|
||||
const std::string &title,
|
||||
const std::string &info) = 0;
|
||||
};
|
||||
|
||||
} // namespace render::hydra
|
||||
} // namespace blender
|
||||
140
blender-5.2.0/source/blender/render/hydra/final_engine.cc
Normal file
140
blender-5.2.0/source/blender/render/hydra/final_engine.cc
Normal file
@@ -0,0 +1,140 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "final_engine.hh"
|
||||
#include "camera.hh"
|
||||
|
||||
#include <pxr/imaging/hd/light.h>
|
||||
#include <pxr/imaging/hd/renderBuffer.h>
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_time.h"
|
||||
#include "BLI_timecode.h"
|
||||
|
||||
#include "BKE_lib_id.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
void FinalEngine::render()
|
||||
{
|
||||
const ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph_);
|
||||
|
||||
char scene_name[MAX_ID_FULL_NAME];
|
||||
BKE_id_full_name_get(scene_name, &scene_->id, 0);
|
||||
|
||||
const RenderData &r = scene_->r;
|
||||
pxr::GfVec4f border(0, 0, 1, 1);
|
||||
if (r.mode & R_BORDER) {
|
||||
border.Set(r.border.xmin,
|
||||
r.border.ymin,
|
||||
r.border.xmax - r.border.xmin,
|
||||
r.border.ymax - r.border.ymin);
|
||||
}
|
||||
pxr::GfVec2i image_res(r.xsch * r.size / 100, r.ysch * r.size / 100);
|
||||
int width = image_res[0] * border[2];
|
||||
int height = image_res[1] * border[3];
|
||||
|
||||
pxr::GfCamera camera = gf_camera(scene_->camera, image_res, border);
|
||||
|
||||
free_camera_delegate_->SetCamera(camera);
|
||||
render_task_delegate_->set_viewport(pxr::GfVec4d(0, 0, width, height));
|
||||
if (light_tasks_delegate_) {
|
||||
light_tasks_delegate_->set_viewport(pxr::GfVec4d(0, 0, width, height));
|
||||
}
|
||||
|
||||
RenderResult *rr = RE_engine_get_result(bl_engine_);
|
||||
RenderLayer *rlayer = static_cast<RenderLayer *>(rr->layers.first);
|
||||
for (RenderPass &rpass : rlayer->passes) {
|
||||
pxr::TfToken *aov_token = aov_tokens_.lookup_ptr(rpass.name);
|
||||
if (!aov_token) {
|
||||
CLOG_WARN(LOG_HYDRA_RENDER, "Couldn't find AOV token for render pass: %s", rpass.name);
|
||||
continue;
|
||||
}
|
||||
render_task_delegate_->add_aov(*aov_token);
|
||||
}
|
||||
if (bl_engine_->type->flag & RE_USE_GPU_CONTEXT) {
|
||||
/* For GPU context engine color and depth AOVs has to be added anyway */
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->color);
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->depth);
|
||||
}
|
||||
|
||||
render_task_delegate_->bind();
|
||||
|
||||
auto t = tasks();
|
||||
|
||||
char elapsed_time[32];
|
||||
double time_begin = BLI_time_now_seconds();
|
||||
float percent_done = 0.0;
|
||||
|
||||
while (true) {
|
||||
engine_->Execute(render_index_.get(), &t);
|
||||
|
||||
if (RE_engine_test_break(bl_engine_)) {
|
||||
break;
|
||||
}
|
||||
|
||||
percent_done = renderer_percent_done();
|
||||
BLI_timecode_string_from_time_simple(
|
||||
elapsed_time, sizeof(elapsed_time), BLI_time_now_seconds() - time_begin);
|
||||
notify_status(percent_done / 100.0,
|
||||
std::string(scene_name) + ": " + view_layer->name,
|
||||
std::string("Render Time: ") + elapsed_time +
|
||||
" | Done: " + std::to_string(int(percent_done)) + "%");
|
||||
|
||||
if (render_task_delegate_->is_converged()) {
|
||||
break;
|
||||
}
|
||||
|
||||
update_render_result(width, height, view_layer->name);
|
||||
}
|
||||
|
||||
update_render_result(width, height, view_layer->name);
|
||||
render_task_delegate_->unbind();
|
||||
}
|
||||
|
||||
void FinalEngine::set_render_setting(const std::string &key, const pxr::VtValue &val)
|
||||
{
|
||||
if (STRPREFIX(key.c_str(), "aovToken:")) {
|
||||
aov_tokens_.add_overwrite(key.substr(key.find(":") + 1),
|
||||
pxr::TfToken(val.UncheckedGet<std::string>()));
|
||||
return;
|
||||
}
|
||||
Engine::set_render_setting(key, val);
|
||||
}
|
||||
|
||||
void FinalEngine::notify_status(float progress, const std::string &title, const std::string &info)
|
||||
{
|
||||
RE_engine_update_progress(bl_engine_, progress);
|
||||
RE_engine_update_stats(bl_engine_, title.c_str(), info.c_str());
|
||||
}
|
||||
|
||||
void FinalEngine::update_render_result(int width, int height, const char *layer_name)
|
||||
{
|
||||
RenderResult *rr = RE_engine_begin_result(bl_engine_, 0, 0, width, height, layer_name, nullptr);
|
||||
|
||||
RenderLayer *rlayer = static_cast<RenderLayer *>(
|
||||
BLI_findstring(&rr->layers, layer_name, offsetof(RenderLayer, name)));
|
||||
|
||||
if (rlayer) {
|
||||
for (RenderPass &rpass : rlayer->passes) {
|
||||
pxr::TfToken *aov_token = aov_tokens_.lookup_ptr(rpass.name);
|
||||
if (aov_token) {
|
||||
render_task_delegate_->read_aov(*aov_token, rpass.ibuf->float_data_for_write());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RE_engine_end_result(bl_engine_, rr, false, false, false);
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
28
blender-5.2.0/source/blender/render/hydra/final_engine.hh
Normal file
28
blender-5.2.0/source/blender/render/hydra/final_engine.hh
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "engine.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class FinalEngine : public Engine {
|
||||
private:
|
||||
Map<std::string, pxr::TfToken> aov_tokens_;
|
||||
|
||||
public:
|
||||
using Engine::Engine;
|
||||
|
||||
void render() override;
|
||||
void set_render_setting(const std::string &key, const pxr::VtValue &val) override;
|
||||
|
||||
protected:
|
||||
void notify_status(float progress, const std::string &title, const std::string &info) override;
|
||||
|
||||
private:
|
||||
void update_render_result(int width, int height, const char *layer_name);
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
@@ -0,0 +1,106 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "light_tasks_delegate.hh"
|
||||
#include "engine.hh"
|
||||
|
||||
#include <pxr/imaging/hd/legacyTaskFactory.h>
|
||||
#include <pxr/imaging/hd/legacyTaskSchema.h>
|
||||
#include <pxr/imaging/hd/retainedDataSource.h>
|
||||
#include <pxr/imaging/hd/sceneIndexObserver.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
LightTasksDelegate::LightTasksDelegate(pxr::HdRenderIndex *render_index,
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index,
|
||||
pxr::SdfPath const &base_id)
|
||||
: render_index_(render_index),
|
||||
task_scene_index_(std::move(task_scene_index)),
|
||||
simple_task_id_(base_id.AppendElementString("simpleTask")),
|
||||
skydome_task_id_(base_id.AppendElementString("skydomeTask"))
|
||||
{
|
||||
publish_simple_task();
|
||||
publish_skydome_task();
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", simple_task_id_.GetText());
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", skydome_task_id_.GetText());
|
||||
}
|
||||
|
||||
void LightTasksDelegate::publish_simple_task()
|
||||
{
|
||||
pxr::HdContainerDataSourceHandle task_ds =
|
||||
pxr::HdLegacyTaskSchema::Builder()
|
||||
.SetFactory(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::HdLegacyTaskFactorySharedPtr>::New(
|
||||
pxr::HdMakeLegacyTaskFactory<pxr::HdxSimpleLightTask>()))
|
||||
.SetParameters(simple_task_params_ds_)
|
||||
.Build();
|
||||
|
||||
task_scene_index_->AddPrims({{simple_task_id_,
|
||||
pxr::HdPrimTypeTokens->task,
|
||||
pxr::HdRetainedContainerDataSource::New(
|
||||
pxr::HdLegacyTaskSchema::GetSchemaToken(), task_ds)}});
|
||||
}
|
||||
|
||||
void LightTasksDelegate::publish_skydome_task()
|
||||
{
|
||||
const pxr::HdRprimCollection collection(pxr::HdTokens->geometry,
|
||||
pxr::HdReprSelector(pxr::HdReprTokens->smoothHull));
|
||||
const pxr::TfTokenVector render_tags = {pxr::HdRenderTagTokens->geometry};
|
||||
|
||||
pxr::HdContainerDataSourceHandle task_ds =
|
||||
pxr::HdLegacyTaskSchema::Builder()
|
||||
.SetFactory(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::HdLegacyTaskFactorySharedPtr>::New(
|
||||
pxr::HdMakeLegacyTaskFactory<pxr::HdxSkydomeTask>()))
|
||||
.SetParameters(skydome_task_params_ds_)
|
||||
.SetCollection(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::HdRprimCollection>::New(collection))
|
||||
.SetRenderTags(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::TfTokenVector>::New(render_tags))
|
||||
.Build();
|
||||
|
||||
task_scene_index_->AddPrims({{skydome_task_id_,
|
||||
pxr::HdPrimTypeTokens->task,
|
||||
pxr::HdRetainedContainerDataSource::New(
|
||||
pxr::HdLegacyTaskSchema::GetSchemaToken(), task_ds)}});
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtr LightTasksDelegate::simple_task()
|
||||
{
|
||||
return render_index_->GetTask(simple_task_id_);
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtr LightTasksDelegate::skydome_task()
|
||||
{
|
||||
/* Note that this task is intended to be the first "Render Task",
|
||||
* so that the AOV's are properly cleared, however it
|
||||
* does not spawn a HdRenderPass. */
|
||||
return render_index_->GetTask(skydome_task_id_);
|
||||
}
|
||||
|
||||
void LightTasksDelegate::set_camera(pxr::SdfPath const &camera_id)
|
||||
{
|
||||
if (simple_task_params_.cameraPath == camera_id && skydome_task_params_.camera == camera_id) {
|
||||
return;
|
||||
}
|
||||
simple_task_params_.cameraPath = camera_id;
|
||||
skydome_task_params_.camera = camera_id;
|
||||
task_scene_index_->DirtyPrims(
|
||||
{{simple_task_id_, pxr::HdLegacyTaskSchema::GetParametersLocator()},
|
||||
{skydome_task_id_, pxr::HdLegacyTaskSchema::GetParametersLocator()}});
|
||||
}
|
||||
|
||||
void LightTasksDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
||||
{
|
||||
if (skydome_task_params_.viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
skydome_task_params_.viewport = viewport;
|
||||
task_scene_index_->DirtyPrims(
|
||||
{{skydome_task_id_, pxr::HdLegacyTaskSchema::GetParametersLocator()}});
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/dataSource.h>
|
||||
#include <pxr/imaging/hd/renderIndex.h>
|
||||
#include <pxr/imaging/hd/retainedSceneIndex.h>
|
||||
#include <pxr/imaging/hd/task.h>
|
||||
#include <pxr/imaging/hdx/simpleLightTask.h>
|
||||
#include <pxr/imaging/hdx/skydomeTask.h>
|
||||
|
||||
#include "render_task_delegate.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
/* Registers a HdxSimpleLightTask and HdxSkydomeTask with the render index. */
|
||||
|
||||
class SimpleLightTaskParamsDataSource final
|
||||
: public pxr::HdTypedSampledDataSource<pxr::HdxSimpleLightTaskParams> {
|
||||
public:
|
||||
HD_DECLARE_DATASOURCE(SimpleLightTaskParamsDataSource);
|
||||
|
||||
pxr::HdxSimpleLightTaskParams params;
|
||||
|
||||
pxr::VtValue GetValue(Time /*t*/) override
|
||||
{
|
||||
return pxr::VtValue(params);
|
||||
}
|
||||
pxr::HdxSimpleLightTaskParams GetTypedValue(Time /*t*/) override
|
||||
{
|
||||
return params;
|
||||
}
|
||||
bool GetContributingSampleTimesForInterval(Time /*start*/,
|
||||
Time /*end*/,
|
||||
std::vector<Time> * /*sampleTimes*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class LightTasksDelegate {
|
||||
public:
|
||||
LightTasksDelegate(pxr::HdRenderIndex *render_index,
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index,
|
||||
pxr::SdfPath const &base_id);
|
||||
|
||||
pxr::HdTaskSharedPtr simple_task();
|
||||
pxr::HdTaskSharedPtr skydome_task();
|
||||
void set_camera(pxr::SdfPath const &camera_id);
|
||||
void set_viewport(pxr::GfVec4d const &viewport);
|
||||
|
||||
private:
|
||||
void publish_simple_task();
|
||||
void publish_skydome_task();
|
||||
|
||||
pxr::HdRenderIndex *render_index_ = nullptr;
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index_;
|
||||
pxr::SdfPath simple_task_id_;
|
||||
pxr::SdfPath skydome_task_id_;
|
||||
SimpleLightTaskParamsDataSource::Handle simple_task_params_ds_ =
|
||||
SimpleLightTaskParamsDataSource::New();
|
||||
RenderTaskParamsDataSource::Handle skydome_task_params_ds_ = RenderTaskParamsDataSource::New();
|
||||
pxr::HdxSimpleLightTaskParams &simple_task_params_ = simple_task_params_ds_->params;
|
||||
pxr::HdxRenderTaskParams &skydome_task_params_ = skydome_task_params_ds_->params;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
16
blender-5.2.0/source/blender/render/hydra/preview_engine.cc
Normal file
16
blender-5.2.0/source/blender/render/hydra/preview_engine.cc
Normal file
@@ -0,0 +1,16 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "preview_engine.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
void PreviewEngine::notify_status(float /*progress*/,
|
||||
const std::string & /*title*/,
|
||||
const std::string & /*info*/)
|
||||
{
|
||||
/* Empty function. */
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
19
blender-5.2.0/source/blender/render/hydra/preview_engine.hh
Normal file
19
blender-5.2.0/source/blender/render/hydra/preview_engine.hh
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "final_engine.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class PreviewEngine : public FinalEngine {
|
||||
public:
|
||||
using FinalEngine::FinalEngine;
|
||||
|
||||
protected:
|
||||
void notify_status(float progress, const std::string &title, const std::string &info) override;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
217
blender-5.2.0/source/blender/render/hydra/python.cc
Normal file
217
blender-5.2.0/source/blender/render/hydra/python.cc
Normal file
@@ -0,0 +1,217 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "final_engine.hh"
|
||||
#include "preview_engine.hh"
|
||||
#include "viewport_engine.hh"
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "bpy_rna.hh"
|
||||
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "hydra/image.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace render::hydra {
|
||||
|
||||
template<typename T> T *pyrna_to_pointer(PyObject *pyobject, const StructRNA *rnatype)
|
||||
{
|
||||
const PointerRNA *ptr = pyrna_struct_as_ptr_or_null(pyobject, rnatype);
|
||||
return (ptr) ? static_cast<T *>(ptr->data) : nullptr;
|
||||
}
|
||||
|
||||
static PyObject *engine_create_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
char *engine_type, *render_delegate_id;
|
||||
if (!PyArg_ParseTuple(args, "Oss", &pyengine, &engine_type, &render_delegate_id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RenderEngine *bl_engine = pyrna_to_pointer<RenderEngine>(pyengine, RNA_RenderEngine);
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %s", engine_type);
|
||||
Engine *engine = nullptr;
|
||||
try {
|
||||
if (STREQ(engine_type, "VIEWPORT")) {
|
||||
engine = new ViewportEngine(bl_engine, render_delegate_id);
|
||||
}
|
||||
else if (STREQ(engine_type, "PREVIEW")) {
|
||||
engine = new PreviewEngine(bl_engine, render_delegate_id);
|
||||
}
|
||||
else {
|
||||
engine = new FinalEngine(bl_engine, render_delegate_id);
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error &e) {
|
||||
CLOG_ERROR(LOG_HYDRA_RENDER, "%s", e.what());
|
||||
}
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %p", engine);
|
||||
return PyLong_FromVoidPtr(engine);
|
||||
}
|
||||
|
||||
static PyObject *engine_free_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
if (!PyArg_ParseTuple(args, "O", &pyengine)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %p", engine);
|
||||
delete engine;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *engine_update_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine, *pydepsgraph, *pycontext;
|
||||
if (!PyArg_ParseTuple(args, "OOO", &pyengine, &pydepsgraph, &pycontext)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
Depsgraph *depsgraph = pyrna_to_pointer<Depsgraph>(pydepsgraph, RNA_Depsgraph);
|
||||
bContext *context = pyrna_to_pointer<bContext>(pycontext, RNA_Context);
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %p", engine);
|
||||
engine->sync(depsgraph, context);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *engine_render_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine;
|
||||
if (!PyArg_ParseTuple(args, "O", &pyengine)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %p", engine);
|
||||
|
||||
/* Allow Blender to execute other Python scripts. */
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
engine->render();
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *engine_view_draw_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine, *pycontext;
|
||||
if (!PyArg_ParseTuple(args, "OO", &pyengine, &pycontext)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ViewportEngine *engine = static_cast<ViewportEngine *>(PyLong_AsVoidPtr(pyengine));
|
||||
bContext *context = pyrna_to_pointer<bContext>(pycontext, RNA_Context);
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %p", engine);
|
||||
|
||||
/* Allow Blender to execute other Python scripts. */
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
engine->render(context);
|
||||
Py_END_ALLOW_THREADS;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static pxr::VtValue get_setting_val(PyObject *pyval)
|
||||
{
|
||||
pxr::VtValue val;
|
||||
if (PyBool_Check(pyval)) {
|
||||
val = Py_IsTrue(pyval);
|
||||
}
|
||||
else if (PyLong_Check(pyval)) {
|
||||
val = PyLong_AsLong(pyval);
|
||||
}
|
||||
else if (PyFloat_Check(pyval)) {
|
||||
val = PyFloat_AsDouble(pyval);
|
||||
}
|
||||
else if (PyUnicode_Check(pyval)) {
|
||||
val = std::string(PyUnicode_AsUTF8(pyval));
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static PyObject *engine_set_render_setting_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pyengine, *pyval;
|
||||
char *key;
|
||||
if (!PyArg_ParseTuple(args, "OsO", &pyengine, &key, &pyval)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Engine *engine = static_cast<Engine *>(PyLong_AsVoidPtr(pyengine));
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "Engine %p: %s", engine, key);
|
||||
engine->set_render_setting(key, get_setting_val(pyval));
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *cache_or_get_image_file_func(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyObject *pycontext, *pyimage;
|
||||
if (!PyArg_ParseTuple(args, "OO", &pycontext, &pyimage)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bContext *context = static_cast<bContext *>(PyLong_AsVoidPtr(pycontext));
|
||||
Image *image = static_cast<Image *>(PyLong_AsVoidPtr(pyimage));
|
||||
|
||||
std::string image_path = io::hydra::cache_or_get_image_file(
|
||||
CTX_data_main(context), CTX_data_scene(context), image, nullptr);
|
||||
return PyC_UnicodeFromStdStr(image_path);
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
{"engine_create", engine_create_func, METH_VARARGS, ""},
|
||||
{"engine_free", engine_free_func, METH_VARARGS, ""},
|
||||
{"engine_update", engine_update_func, METH_VARARGS, ""},
|
||||
{"engine_render", engine_render_func, METH_VARARGS, ""},
|
||||
{"engine_view_draw", engine_view_draw_func, METH_VARARGS, ""},
|
||||
{"engine_set_render_setting", engine_set_render_setting_func, METH_VARARGS, ""},
|
||||
|
||||
{"cache_or_get_image_file", cache_or_get_image_file_func, METH_VARARGS, ""},
|
||||
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
static PyModuleDef module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_bpy_hydra",
|
||||
"Hydra render API",
|
||||
-1,
|
||||
methods,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
} // namespace render::hydra
|
||||
|
||||
PyObject *BPyInit_hydra();
|
||||
|
||||
PyObject *BPyInit_hydra()
|
||||
{
|
||||
PyObject *mod = PyModule_Create(&render::hydra::module);
|
||||
return mod;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,352 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "render_task_delegate.hh"
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
# include "GPU_context.hh"
|
||||
# include <epoxy/gl.h>
|
||||
#endif
|
||||
|
||||
#include <pxr/imaging/hd/legacyTaskFactory.h>
|
||||
#include <pxr/imaging/hd/legacyTaskSchema.h>
|
||||
#include <pxr/imaging/hd/renderBuffer.h>
|
||||
#include <pxr/imaging/hd/renderBufferSchema.h>
|
||||
#include <pxr/imaging/hd/renderDelegate.h>
|
||||
#include <pxr/imaging/hd/retainedDataSource.h>
|
||||
#include <pxr/imaging/hd/sceneIndexObserver.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
#include <pxr/imaging/hdx/renderTask.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
#include "engine.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
RenderTaskDelegate::RenderTaskDelegate(pxr::HdRenderIndex *render_index,
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index,
|
||||
pxr::SdfPath const &base_id)
|
||||
: render_index_(render_index),
|
||||
task_scene_index_(std::move(task_scene_index)),
|
||||
base_id_(base_id),
|
||||
task_id_(base_id.AppendElementString("task"))
|
||||
{
|
||||
task_params_.enableLighting = true;
|
||||
task_params_.alphaThreshold = 0.1f;
|
||||
|
||||
/* Disable this so Metal and OpenGL match in Storm render tests, only
|
||||
* the former seems to use multisample. */
|
||||
task_params_.useAovMultiSample = false;
|
||||
|
||||
publish_task();
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", task_id_.GetText());
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::publish_task()
|
||||
{
|
||||
const pxr::HdRprimCollection collection(pxr::HdTokens->geometry,
|
||||
pxr::HdReprSelector(pxr::HdReprTokens->smoothHull));
|
||||
const pxr::TfTokenVector render_tags = {pxr::HdRenderTagTokens->geometry};
|
||||
|
||||
pxr::HdContainerDataSourceHandle task_ds =
|
||||
pxr::HdLegacyTaskSchema::Builder()
|
||||
.SetFactory(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::HdLegacyTaskFactorySharedPtr>::New(
|
||||
pxr::HdMakeLegacyTaskFactory<pxr::HdxRenderTask>()))
|
||||
.SetParameters(task_params_ds_)
|
||||
.SetCollection(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::HdRprimCollection>::New(collection))
|
||||
.SetRenderTags(
|
||||
pxr::HdRetainedTypedSampledDataSource<pxr::TfTokenVector>::New(render_tags))
|
||||
.Build();
|
||||
|
||||
task_scene_index_->AddPrims({{task_id_,
|
||||
pxr::HdPrimTypeTokens->task,
|
||||
pxr::HdRetainedContainerDataSource::New(
|
||||
pxr::HdLegacyTaskSchema::GetSchemaToken(), task_ds)}});
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::dirty_task_params()
|
||||
{
|
||||
task_scene_index_->DirtyPrims({{task_id_, pxr::HdLegacyTaskSchema::GetParametersLocator()}});
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::publish_buffer(pxr::SdfPath const &buf_id,
|
||||
pxr::HdRenderBufferDescriptor const &desc)
|
||||
{
|
||||
pxr::HdContainerDataSourceHandle buffer_ds =
|
||||
pxr::HdRenderBufferSchema::Builder()
|
||||
.SetDimensions(pxr::HdRetainedTypedSampledDataSource<pxr::GfVec3i>::New(desc.dimensions))
|
||||
.SetFormat(pxr::HdRetainedTypedSampledDataSource<pxr::HdFormat>::New(desc.format))
|
||||
.SetMultiSampled(pxr::HdRetainedTypedSampledDataSource<bool>::New(desc.multiSampled))
|
||||
.Build();
|
||||
|
||||
task_scene_index_->AddPrims({{buf_id,
|
||||
pxr::HdPrimTypeTokens->renderBuffer,
|
||||
pxr::HdRetainedContainerDataSource::New(
|
||||
pxr::HdRenderBufferSchema::GetSchemaToken(), buffer_ds)}});
|
||||
}
|
||||
|
||||
pxr::HdTaskSharedPtr RenderTaskDelegate::task()
|
||||
{
|
||||
return render_index_->GetTask(task_id_);
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::set_camera(pxr::SdfPath const &camera_id)
|
||||
{
|
||||
if (task_params_.camera == camera_id) {
|
||||
return;
|
||||
}
|
||||
task_params_.camera = camera_id;
|
||||
dirty_task_params();
|
||||
}
|
||||
|
||||
bool RenderTaskDelegate::is_converged()
|
||||
{
|
||||
return static_cast<pxr::HdxRenderTask *>(task().get())->IsConverged();
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
||||
{
|
||||
if (task_params_.viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
task_params_.viewport = viewport;
|
||||
dirty_task_params();
|
||||
|
||||
int w = viewport[2] - viewport[0];
|
||||
int h = viewport[3] - viewport[1];
|
||||
for (auto &it : buffer_descriptors_) {
|
||||
it.second.dimensions = pxr::GfVec3i(w, h, 1);
|
||||
publish_buffer(it.first, it.second);
|
||||
task_scene_index_->DirtyPrims({{it.first, pxr::HdRenderBufferSchema::GetDimensionsLocator()}});
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::add_aov(pxr::TfToken const &aov_key)
|
||||
{
|
||||
pxr::SdfPath buf_id = buffer_id(aov_key);
|
||||
if (buffer_descriptors_.find(buf_id) != buffer_descriptors_.end()) {
|
||||
return;
|
||||
}
|
||||
pxr::HdAovDescriptor aov_desc = render_index_->GetRenderDelegate()->GetDefaultAovDescriptor(
|
||||
aov_key);
|
||||
|
||||
if (aov_desc.format == pxr::HdFormatInvalid) {
|
||||
CLOG_ERROR(LOG_HYDRA_RENDER, "Invalid AOV: %s", aov_key.GetText());
|
||||
return;
|
||||
}
|
||||
if (!ELEM(
|
||||
pxr::HdGetComponentFormat(aov_desc.format), pxr::HdFormatFloat32, pxr::HdFormatFloat16))
|
||||
{
|
||||
CLOG_WARN(LOG_HYDRA_RENDER,
|
||||
"Unsupported data format %s for AOV %s",
|
||||
pxr::TfEnum::GetName(aov_desc.format).c_str(),
|
||||
aov_key.GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
int w = task_params_.viewport[2] - task_params_.viewport[0];
|
||||
int h = task_params_.viewport[3] - task_params_.viewport[1];
|
||||
pxr::HdRenderBufferDescriptor desc(
|
||||
pxr::GfVec3i(w, h, 1), aov_desc.format, aov_desc.multiSampled);
|
||||
buffer_descriptors_[buf_id] = desc;
|
||||
publish_buffer(buf_id, desc);
|
||||
|
||||
pxr::HdRenderPassAovBinding binding;
|
||||
binding.aovName = aov_key;
|
||||
binding.renderBufferId = buf_id;
|
||||
binding.aovSettings = aov_desc.aovSettings;
|
||||
binding.clearValue = aov_desc.clearValue;
|
||||
task_params_.aovBindings.push_back(binding);
|
||||
dirty_task_params();
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", aov_key.GetText());
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, void *data)
|
||||
{
|
||||
pxr::HdRenderBuffer *buffer = static_cast<pxr::HdRenderBuffer *>(
|
||||
render_index_->GetBprim(pxr::HdPrimTypeTokens->renderBuffer, buffer_id(aov_key)));
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::HdFormat format = buffer->GetFormat();
|
||||
size_t len = buffer->GetWidth() * buffer->GetHeight() * pxr::HdGetComponentCount(format);
|
||||
if (pxr::HdGetComponentFormat(format) == pxr::HdFormatFloat32) {
|
||||
void *buf_data = buffer->Map();
|
||||
memcpy(data, buf_data, len * sizeof(float));
|
||||
buffer->Unmap();
|
||||
}
|
||||
else if (pxr::HdGetComponentFormat(format) == pxr::HdFormatFloat16) {
|
||||
Eigen::half *buf_data = (Eigen::half *)buffer->Map();
|
||||
float *fdata = static_cast<float *>(data);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
fdata[i] = buf_data[i];
|
||||
}
|
||||
buffer->Unmap();
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
pxr::HdRenderBuffer *RenderTaskDelegate::get_aov_buffer(pxr::TfToken const &aov_key)
|
||||
{
|
||||
return (pxr::HdRenderBuffer *)render_index_->GetBprim(pxr::HdPrimTypeTokens->renderBuffer,
|
||||
buffer_id(aov_key));
|
||||
}
|
||||
|
||||
void RenderTaskDelegate::bind() {}
|
||||
|
||||
void RenderTaskDelegate::unbind() {}
|
||||
|
||||
pxr::SdfPath RenderTaskDelegate::buffer_id(pxr::TfToken const &aov_key) const
|
||||
{
|
||||
return base_id_.AppendElementString("aov_" + aov_key.GetString());
|
||||
}
|
||||
|
||||
GPURenderTaskDelegate::~GPURenderTaskDelegate()
|
||||
{
|
||||
unbind();
|
||||
if (tex_color_) {
|
||||
GPU_texture_free(tex_color_);
|
||||
}
|
||||
if (tex_depth_) {
|
||||
GPU_texture_free(tex_depth_);
|
||||
}
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::set_viewport(pxr::GfVec4d const &viewport)
|
||||
{
|
||||
if (task_params_.viewport == viewport) {
|
||||
return;
|
||||
}
|
||||
task_params_.viewport = viewport;
|
||||
dirty_task_params();
|
||||
|
||||
if (tex_color_) {
|
||||
GPU_texture_free(tex_color_);
|
||||
tex_color_ = nullptr;
|
||||
add_aov(pxr::HdAovTokens->color);
|
||||
}
|
||||
if (tex_depth_) {
|
||||
GPU_texture_free(tex_depth_);
|
||||
tex_depth_ = nullptr;
|
||||
add_aov(pxr::HdAovTokens->depth);
|
||||
}
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::add_aov(pxr::TfToken const &aov_key)
|
||||
{
|
||||
gpu::TextureFormat format;
|
||||
gpu::Texture **tex;
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
format = gpu::TextureFormat::SFLOAT_32_32_32_32;
|
||||
tex = &tex_color_;
|
||||
}
|
||||
else if (aov_key == pxr::HdAovTokens->depth) {
|
||||
format = gpu::TextureFormat::SFLOAT_32_DEPTH;
|
||||
tex = &tex_depth_;
|
||||
}
|
||||
else {
|
||||
CLOG_ERROR(LOG_HYDRA_RENDER, "Invalid AOV: %s", aov_key.GetText());
|
||||
return;
|
||||
}
|
||||
|
||||
if (*tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
*tex = GPU_texture_create_2d(("tex_render_hydra_" + aov_key.GetString()).c_str(),
|
||||
task_params_.viewport[2] - task_params_.viewport[0],
|
||||
task_params_.viewport[3] - task_params_.viewport[1],
|
||||
1,
|
||||
format,
|
||||
GPU_TEXTURE_USAGE_GENERAL,
|
||||
nullptr);
|
||||
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "%s", aov_key.GetText());
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::read_aov(pxr::TfToken const &aov_key, void *data)
|
||||
{
|
||||
gpu::Texture *tex = nullptr;
|
||||
int c;
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
tex = tex_color_;
|
||||
c = 4;
|
||||
}
|
||||
else if (aov_key == pxr::HdAovTokens->depth) {
|
||||
tex = tex_depth_;
|
||||
c = 1;
|
||||
}
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
int w = GPU_texture_width(tex), h = GPU_texture_height(tex);
|
||||
void *tex_data = GPU_texture_read(tex, GPU_DATA_FLOAT, 0);
|
||||
memcpy(data, tex_data, sizeof(float) * w * h * c);
|
||||
MEM_delete_void(tex_data);
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::bind()
|
||||
{
|
||||
if (!framebuffer_) {
|
||||
framebuffer_ = GPU_framebuffer_create("fb_render_hydra");
|
||||
}
|
||||
GPU_framebuffer_ensure_config(
|
||||
&framebuffer_, {GPU_ATTACHMENT_TEXTURE(tex_depth_), GPU_ATTACHMENT_TEXTURE(tex_color_)});
|
||||
GPU_framebuffer_bind(framebuffer_);
|
||||
|
||||
GPU_framebuffer_clear_color_depth(framebuffer_, {0.0, 0.0, 0.0, 0.0}, 1.0f);
|
||||
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
/* Workaround missing/buggy VAOs in hgiGL and hdSt. For OpenGL compatibility
|
||||
* profile this is not a problem, but for core profile it is. */
|
||||
if (VAO_ == 0 && GPU_backend_get_type() == GPU_BACKEND_OPENGL) {
|
||||
glGenVertexArrays(1, &VAO_);
|
||||
glBindVertexArray(VAO_);
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(VAO_);
|
||||
#endif
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "bind");
|
||||
}
|
||||
|
||||
void GPURenderTaskDelegate::unbind()
|
||||
{
|
||||
#ifdef WITH_OPENGL_BACKEND
|
||||
if (VAO_) {
|
||||
glDeleteVertexArrays(1, &VAO_);
|
||||
VAO_ = 0;
|
||||
}
|
||||
#endif
|
||||
if (framebuffer_) {
|
||||
GPU_framebuffer_free(framebuffer_);
|
||||
framebuffer_ = nullptr;
|
||||
}
|
||||
CLOG_DEBUG(LOG_HYDRA_RENDER, "unbind");
|
||||
}
|
||||
|
||||
gpu::Texture *GPURenderTaskDelegate::get_aov_texture(pxr::TfToken const &aov_key)
|
||||
{
|
||||
if (aov_key == pxr::HdAovTokens->color) {
|
||||
return tex_color_;
|
||||
}
|
||||
if (aov_key == pxr::HdAovTokens->depth) {
|
||||
return tex_depth_;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/dataSource.h>
|
||||
#include <pxr/imaging/hd/renderIndex.h>
|
||||
#include <pxr/imaging/hd/retainedSceneIndex.h>
|
||||
#include <pxr/imaging/hd/task.h>
|
||||
#include <pxr/imaging/hdx/renderSetupTask.h>
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
/* Registers a HdxRenderTask with the render index as a task prim. */
|
||||
|
||||
class RenderTaskParamsDataSource final
|
||||
: public pxr::HdTypedSampledDataSource<pxr::HdxRenderTaskParams> {
|
||||
public:
|
||||
HD_DECLARE_DATASOURCE(RenderTaskParamsDataSource);
|
||||
|
||||
pxr::HdxRenderTaskParams params;
|
||||
|
||||
pxr::VtValue GetValue(Time /*shutterOffset*/) override
|
||||
{
|
||||
return pxr::VtValue(params);
|
||||
}
|
||||
pxr::HdxRenderTaskParams GetTypedValue(Time /*shutterOffset*/) override
|
||||
{
|
||||
return params;
|
||||
}
|
||||
bool GetContributingSampleTimesForInterval(Time /*start*/,
|
||||
Time /*end*/,
|
||||
std::vector<Time> * /*sampleTimes*/) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class RenderTaskDelegate {
|
||||
protected:
|
||||
pxr::HdRenderIndex *render_index_ = nullptr;
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index_;
|
||||
pxr::SdfPath base_id_;
|
||||
pxr::SdfPath task_id_;
|
||||
RenderTaskParamsDataSource::Handle task_params_ds_ = RenderTaskParamsDataSource::New();
|
||||
pxr::HdxRenderTaskParams &task_params_ = task_params_ds_->params;
|
||||
pxr::TfHashMap<pxr::SdfPath, pxr::HdRenderBufferDescriptor, pxr::SdfPath::Hash>
|
||||
buffer_descriptors_;
|
||||
|
||||
public:
|
||||
RenderTaskDelegate(pxr::HdRenderIndex *render_index,
|
||||
pxr::HdRetainedSceneIndexRefPtr task_scene_index,
|
||||
pxr::SdfPath const &base_id);
|
||||
virtual ~RenderTaskDelegate() = default;
|
||||
|
||||
pxr::HdTaskSharedPtr task();
|
||||
void set_camera(pxr::SdfPath const &camera_id);
|
||||
bool is_converged();
|
||||
virtual void set_viewport(pxr::GfVec4d const &viewport);
|
||||
virtual void add_aov(pxr::TfToken const &aov_key);
|
||||
virtual void read_aov(pxr::TfToken const &aov_key, void *data);
|
||||
pxr::HdRenderBuffer *get_aov_buffer(pxr::TfToken const &aov_key);
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
|
||||
protected:
|
||||
pxr::SdfPath buffer_id(pxr::TfToken const &aov_key) const;
|
||||
|
||||
void publish_task();
|
||||
void dirty_task_params();
|
||||
void publish_buffer(pxr::SdfPath const &buf_id, pxr::HdRenderBufferDescriptor const &desc);
|
||||
};
|
||||
|
||||
class GPURenderTaskDelegate : public RenderTaskDelegate {
|
||||
private:
|
||||
gpu::FrameBuffer *framebuffer_ = nullptr;
|
||||
gpu::Texture *tex_color_ = nullptr;
|
||||
gpu::Texture *tex_depth_ = nullptr;
|
||||
unsigned int VAO_ = 0;
|
||||
|
||||
public:
|
||||
using RenderTaskDelegate::RenderTaskDelegate;
|
||||
~GPURenderTaskDelegate() override;
|
||||
|
||||
void set_viewport(pxr::GfVec4d const &viewport) override;
|
||||
void add_aov(pxr::TfToken const &aov_key) override;
|
||||
void read_aov(pxr::TfToken const &aov_key, void *data) override;
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
gpu::Texture *get_aov_texture(pxr::TfToken const &aov_key);
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
306
blender-5.2.0/source/blender/render/hydra/viewport_engine.cc
Normal file
306
blender-5.2.0/source/blender/render/hydra/viewport_engine.cc
Normal file
@@ -0,0 +1,306 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "viewport_engine.hh"
|
||||
#include "camera.hh"
|
||||
|
||||
#include <pxr/base/gf/camera.h>
|
||||
#include <pxr/imaging/glf/drawTarget.h>
|
||||
#include <pxr/usd/usdGeom/camera.h>
|
||||
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_vec_types.h" /* This include must be before `BKE_camera.h` due to `rctf` type. */
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_time.h"
|
||||
#include "BLI_timecode.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
#include "BKE_context.hh"
|
||||
|
||||
#include "GPU_matrix.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "RE_engine.h"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
struct ViewSettings {
|
||||
int screen_width;
|
||||
int screen_height;
|
||||
pxr::GfVec4i border;
|
||||
pxr::GfCamera camera;
|
||||
|
||||
ViewSettings(bContext *context);
|
||||
|
||||
int width();
|
||||
int height();
|
||||
};
|
||||
|
||||
ViewSettings::ViewSettings(bContext *context)
|
||||
{
|
||||
View3D *view3d = CTX_wm_view3d(context);
|
||||
RegionView3D *region_data = static_cast<RegionView3D *>(CTX_wm_region_data(context));
|
||||
ARegion *region = CTX_wm_region(context);
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(context);
|
||||
Scene *scene = DEG_get_evaluated_scene(depsgraph);
|
||||
|
||||
screen_width = region->winx;
|
||||
screen_height = region->winy;
|
||||
|
||||
/* Getting render border. */
|
||||
int x1 = 0, y1 = 0;
|
||||
int x2 = screen_width, y2 = screen_height;
|
||||
|
||||
if (region_data->persp == RV3D_CAMOB) {
|
||||
Object *camera_obj = scene->camera;
|
||||
if ((scene->r.mode & R_BORDER) && camera_obj && camera_obj->type == OB_CAMERA) {
|
||||
float camera_points[4][3];
|
||||
BKE_camera_view_frame(scene, id_cast<Camera *>(camera_obj->data), camera_points);
|
||||
|
||||
float screen_points[4][2];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
float world_location[] = {
|
||||
camera_points[i][0], camera_points[i][1], camera_points[i][2], 1.0f};
|
||||
mul_m4_v4(camera_obj->object_to_world().ptr(), world_location);
|
||||
mul_m4_v4(region_data->persmat, world_location);
|
||||
|
||||
if (world_location[3] > 0.0) {
|
||||
screen_points[i][0] = screen_width * 0.5f +
|
||||
screen_width * 0.5f * (world_location[0] / world_location[3]);
|
||||
screen_points[i][1] = screen_height * 0.5f +
|
||||
screen_height * 0.5f * (world_location[1] / world_location[3]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Getting camera view region. */
|
||||
float x1_f = std::min(
|
||||
{screen_points[0][0], screen_points[1][0], screen_points[2][0], screen_points[3][0]});
|
||||
float x2_f = std::max(
|
||||
{screen_points[0][0], screen_points[1][0], screen_points[2][0], screen_points[3][0]});
|
||||
float y1_f = std::min(
|
||||
{screen_points[0][1], screen_points[1][1], screen_points[2][1], screen_points[3][1]});
|
||||
float y2_f = std::max(
|
||||
{screen_points[0][1], screen_points[1][1], screen_points[2][1], screen_points[3][1]});
|
||||
|
||||
/* Adjusting region to border. */
|
||||
float x = x1_f, y = y1_f;
|
||||
float dx = x2_f - x1_f, dy = y2_f - y1_f;
|
||||
|
||||
x1 = x + scene->r.border.xmin * dx;
|
||||
x2 = x + scene->r.border.xmax * dx;
|
||||
y1 = y + scene->r.border.ymin * dy;
|
||||
y2 = y + scene->r.border.ymax * dy;
|
||||
|
||||
/* Adjusting to region screen resolution. */
|
||||
x1 = std::max(std::min(x1, screen_width), 0);
|
||||
x2 = std::max(std::min(x2, screen_width), 0);
|
||||
y1 = std::max(std::min(y1, screen_height), 0);
|
||||
y2 = std::max(std::min(y2, screen_height), 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (view3d->flag2 & V3D_RENDER_BORDER) {
|
||||
x1 = view3d->render_border.xmin * screen_width;
|
||||
x2 = view3d->render_border.xmax * screen_width;
|
||||
y1 = view3d->render_border.ymin * screen_height;
|
||||
y2 = view3d->render_border.ymax * screen_height;
|
||||
}
|
||||
}
|
||||
|
||||
border = pxr::GfVec4i(x1, y1, x2, y2);
|
||||
|
||||
camera = gf_camera(depsgraph,
|
||||
view3d,
|
||||
region,
|
||||
pxr::GfVec4f(float(border[0]) / screen_width,
|
||||
float(border[1]) / screen_height,
|
||||
float(width()) / screen_width,
|
||||
float(height()) / screen_height));
|
||||
}
|
||||
|
||||
int ViewSettings::width()
|
||||
{
|
||||
return border[2] - border[0];
|
||||
}
|
||||
|
||||
int ViewSettings::height()
|
||||
{
|
||||
return border[3] - border[1];
|
||||
}
|
||||
|
||||
DrawTexture::DrawTexture()
|
||||
{
|
||||
float coords[8] = {0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0};
|
||||
|
||||
GPUVertFormat format = {0};
|
||||
GPU_vertformat_attr_add(&format, "pos", gpu::VertAttrType::SFLOAT_32_32);
|
||||
GPU_vertformat_attr_add(&format, "texCoord", gpu::VertAttrType::SFLOAT_32_32);
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(format);
|
||||
GPU_vertbuf_data_alloc(*vbo, 4);
|
||||
GPU_vertbuf_attr_fill(vbo, 0, coords);
|
||||
GPU_vertbuf_attr_fill(vbo, 1, coords);
|
||||
|
||||
batch_ = GPU_batch_create_ex(GPU_PRIM_TRI_FAN, vbo, nullptr, GPU_BATCH_OWNS_VBO);
|
||||
}
|
||||
|
||||
DrawTexture::~DrawTexture()
|
||||
{
|
||||
if (texture_) {
|
||||
GPU_texture_free(texture_);
|
||||
}
|
||||
GPU_batch_discard(batch_);
|
||||
}
|
||||
|
||||
void DrawTexture::create_from_buffer(pxr::HdRenderBuffer *buffer)
|
||||
{
|
||||
if (buffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpu::TextureFormat texture_format;
|
||||
eGPUDataFormat data_format;
|
||||
|
||||
if (buffer->GetFormat() == pxr::HdFormat::HdFormatFloat16Vec4) {
|
||||
texture_format = gpu::TextureFormat::SFLOAT_16_16_16_16;
|
||||
data_format = GPU_DATA_HALF_FLOAT;
|
||||
}
|
||||
else {
|
||||
texture_format = gpu::TextureFormat::SFLOAT_32_32_32_32;
|
||||
data_format = GPU_DATA_FLOAT;
|
||||
}
|
||||
|
||||
if (texture_ && (GPU_texture_width(texture_) != buffer->GetWidth() ||
|
||||
GPU_texture_height(texture_) != buffer->GetHeight() ||
|
||||
GPU_texture_format(texture_) != texture_format))
|
||||
{
|
||||
GPU_texture_free(texture_);
|
||||
texture_ = nullptr;
|
||||
}
|
||||
|
||||
if (texture_ == nullptr) {
|
||||
texture_ = GPU_texture_create_2d("tex_hydra_render_viewport",
|
||||
buffer->GetWidth(),
|
||||
buffer->GetHeight(),
|
||||
1,
|
||||
texture_format,
|
||||
GPU_TEXTURE_USAGE_GENERAL,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void *data = buffer->Map();
|
||||
GPU_texture_update(texture_, data_format, data);
|
||||
buffer->Unmap();
|
||||
}
|
||||
|
||||
void DrawTexture::draw(gpu::Shader *shader, const pxr::GfVec4d &viewport, gpu::Texture *tex)
|
||||
{
|
||||
if (!tex) {
|
||||
tex = texture_;
|
||||
}
|
||||
int slot = GPU_shader_get_sampler_binding(shader, "image");
|
||||
GPU_texture_bind(tex, slot);
|
||||
GPU_shader_uniform_1i(shader, "image", slot);
|
||||
|
||||
GPU_matrix_push();
|
||||
GPU_matrix_translate_2f(viewport[0], viewport[1]);
|
||||
GPU_matrix_scale_2f(viewport[2] - viewport[0], viewport[3] - viewport[1]);
|
||||
GPU_batch_set_shader(batch_, shader);
|
||||
GPU_batch_draw(batch_);
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
|
||||
gpu::Texture *DrawTexture::texture() const
|
||||
{
|
||||
return texture_;
|
||||
}
|
||||
|
||||
void ViewportEngine::render()
|
||||
{
|
||||
ViewSettings view_settings(context_);
|
||||
if (view_settings.width() * view_settings.height() == 0) {
|
||||
return;
|
||||
};
|
||||
|
||||
free_camera_delegate_->SetCamera(view_settings.camera);
|
||||
|
||||
pxr::GfVec4d viewport(0.0, 0.0, view_settings.width(), view_settings.height());
|
||||
render_task_delegate_->set_viewport(viewport);
|
||||
if (light_tasks_delegate_) {
|
||||
light_tasks_delegate_->set_viewport(viewport);
|
||||
}
|
||||
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->color);
|
||||
render_task_delegate_->add_aov(pxr::HdAovTokens->depth);
|
||||
|
||||
gpu::FrameBuffer *view_framebuffer = GPU_framebuffer_active_get();
|
||||
render_task_delegate_->bind();
|
||||
|
||||
auto t = tasks();
|
||||
engine_->Execute(render_index_.get(), &t);
|
||||
|
||||
render_task_delegate_->unbind();
|
||||
|
||||
GPU_framebuffer_bind(view_framebuffer);
|
||||
gpu::Shader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE);
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
pxr::GfVec4d draw_viewport(view_settings.border[0],
|
||||
view_settings.border[1],
|
||||
view_settings.border[2],
|
||||
view_settings.border[3]);
|
||||
GPURenderTaskDelegate *gpu_task = dynamic_cast<GPURenderTaskDelegate *>(
|
||||
render_task_delegate_.get());
|
||||
if (gpu_task) {
|
||||
draw_texture_.draw(shader, draw_viewport, gpu_task->get_aov_texture(pxr::HdAovTokens->color));
|
||||
}
|
||||
else {
|
||||
draw_texture_.create_from_buffer(
|
||||
render_task_delegate_->get_aov_buffer(pxr::HdAovTokens->color));
|
||||
draw_texture_.draw(shader, draw_viewport);
|
||||
}
|
||||
|
||||
GPU_shader_unbind();
|
||||
|
||||
if (renderer_percent_done() == 0.0f) {
|
||||
time_begin_ = BLI_time_now_seconds();
|
||||
}
|
||||
|
||||
char elapsed_time[32];
|
||||
|
||||
BLI_timecode_string_from_time_simple(
|
||||
elapsed_time, sizeof(elapsed_time), BLI_time_now_seconds() - time_begin_);
|
||||
|
||||
float percent_done = renderer_percent_done();
|
||||
if (!render_task_delegate_->is_converged()) {
|
||||
notify_status(percent_done / 100.0,
|
||||
std ::string("Time: ") + elapsed_time +
|
||||
" | Done: " + std::to_string(int(percent_done)) + "%",
|
||||
"Render");
|
||||
bl_engine_->flag |= RE_ENGINE_DO_DRAW;
|
||||
}
|
||||
else {
|
||||
notify_status(percent_done / 100.0, std::string("Time: ") + elapsed_time, "Rendering Done");
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportEngine::render(bContext *context)
|
||||
{
|
||||
context_ = context;
|
||||
render();
|
||||
}
|
||||
|
||||
void ViewportEngine::notify_status(float /*progress*/,
|
||||
const std::string &info,
|
||||
const std::string &status)
|
||||
{
|
||||
RE_engine_update_stats(bl_engine_, status.c_str(), info.c_str());
|
||||
}
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
48
blender-5.2.0/source/blender/render/hydra/viewport_engine.hh
Normal file
48
blender-5.2.0/source/blender/render/hydra/viewport_engine.hh
Normal file
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <pxr/imaging/hd/renderBuffer.h>
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_shader.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "engine.hh"
|
||||
|
||||
namespace blender::render::hydra {
|
||||
|
||||
class DrawTexture {
|
||||
private:
|
||||
gpu::Texture *texture_ = nullptr;
|
||||
gpu::Batch *batch_;
|
||||
|
||||
public:
|
||||
DrawTexture();
|
||||
~DrawTexture();
|
||||
|
||||
void create_from_buffer(pxr::HdRenderBuffer *buffer);
|
||||
void draw(gpu::Shader *shader, const pxr::GfVec4d &viewport, gpu::Texture *tex = nullptr);
|
||||
gpu::Texture *texture() const;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
class ViewportEngine : public Engine {
|
||||
private:
|
||||
double time_begin_;
|
||||
DrawTexture draw_texture_;
|
||||
|
||||
public:
|
||||
using Engine::Engine;
|
||||
|
||||
void render() override;
|
||||
void render(bContext *context);
|
||||
|
||||
protected:
|
||||
void notify_status(float progress, const std::string &info, const std::string &status) override;
|
||||
};
|
||||
|
||||
} // namespace blender::render::hydra
|
||||
1091
blender-5.2.0/source/blender/render/intern/bake.cc
Normal file
1091
blender-5.2.0/source/blender/render/intern/bake.cc
Normal file
File diff suppressed because it is too large
Load Diff
860
blender-5.2.0/source/blender/render/intern/compositor.cc
Normal file
860
blender-5.2.0/source/blender/render/intern/compositor.cc
Normal file
@@ -0,0 +1,860 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_memory_utils.hh"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_scene_runtime.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
#include "DRW_render.hh"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_conversion_operation.hh"
|
||||
#include "COM_domain.hh"
|
||||
#include "COM_node_group_operation.hh"
|
||||
#include "COM_realize_on_domain_operation.hh"
|
||||
#include "COM_render_context.hh"
|
||||
#include "COM_result.hh"
|
||||
|
||||
#include "NOD_eval_log.hh"
|
||||
|
||||
#include "RE_compositor.hh"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_texture_pool.hh"
|
||||
|
||||
#include "render_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace render {
|
||||
|
||||
/**
|
||||
* Render Context Data
|
||||
*
|
||||
* Stored separately from the context so we can update it without losing any cached
|
||||
* data from the context.
|
||||
*/
|
||||
class ContextInputData {
|
||||
public:
|
||||
const Render *render;
|
||||
const Main *main;
|
||||
const Scene *scene;
|
||||
const RenderData *render_data;
|
||||
const bNodeTree *node_tree;
|
||||
std::string view_name;
|
||||
compositor::RenderContext *render_context;
|
||||
compositor::NodeGroupOutputTypes needed_outputs;
|
||||
|
||||
ContextInputData(const Render *render,
|
||||
const Main &main,
|
||||
const Scene &scene,
|
||||
const RenderData &render_data,
|
||||
const bNodeTree &node_tree,
|
||||
const char *view_name,
|
||||
compositor::RenderContext *render_context,
|
||||
compositor::NodeGroupOutputTypes needed_outputs)
|
||||
: render(render),
|
||||
main(&main),
|
||||
scene(&scene),
|
||||
render_data(&render_data),
|
||||
node_tree(&node_tree),
|
||||
view_name(view_name),
|
||||
render_context(render_context),
|
||||
needed_outputs(needed_outputs)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/* Render Context Data */
|
||||
|
||||
class Context : public compositor::Context {
|
||||
private:
|
||||
/* Input data. */
|
||||
ContextInputData input_data_;
|
||||
|
||||
/* Cached GPU and CPU passes that the compositor took ownership of. Those had their reference
|
||||
* count incremented when accessed and need to be freed/have their reference count decremented
|
||||
* when destroying the context. */
|
||||
Vector<gpu::Texture *> cached_gpu_passes_;
|
||||
Vector<ImBuf *> cached_cpu_passes_;
|
||||
|
||||
/* True if GPU compute is supported and can be used, if false, we fallback to CPU. */
|
||||
bool gpu_supported_ = true;
|
||||
|
||||
public:
|
||||
Context(compositor::StaticCacheManager &cache_manager, const ContextInputData &input_data)
|
||||
: compositor::Context(cache_manager), input_data_(input_data)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Context()
|
||||
{
|
||||
for (gpu::Texture *pass : cached_gpu_passes_) {
|
||||
GPU_texture_free(pass);
|
||||
}
|
||||
for (ImBuf *pass : cached_cpu_passes_) {
|
||||
IMB_freeImBuf(pass);
|
||||
}
|
||||
}
|
||||
|
||||
const Main &get_main() const override
|
||||
{
|
||||
return *input_data_.main;
|
||||
}
|
||||
|
||||
const Scene &get_scene() const override
|
||||
{
|
||||
return *input_data_.scene;
|
||||
}
|
||||
|
||||
void set_gpu_supported(const bool supported)
|
||||
{
|
||||
gpu_supported_ = supported;
|
||||
}
|
||||
|
||||
bool use_gpu() const override
|
||||
{
|
||||
return gpu_supported_ &&
|
||||
this->get_render_data().compositor_device == SCE_COMPOSITOR_DEVICE_GPU;
|
||||
}
|
||||
|
||||
compositor::NodeGroupOutputTypes needed_outputs() const
|
||||
{
|
||||
return input_data_.needed_outputs;
|
||||
}
|
||||
|
||||
const RenderData &get_render_data() const override
|
||||
{
|
||||
return *(input_data_.render_data);
|
||||
}
|
||||
|
||||
int2 get_render_size() const
|
||||
{
|
||||
Render *render = RE_GetSceneRender(input_data_.scene);
|
||||
RenderResult *render_result = RE_AcquireResultRead(render);
|
||||
|
||||
/* If a render result already exist, use its size, since the compositor operates on the render
|
||||
* settings at which the render happened. Otherwise, use the size from the render data. */
|
||||
int2 size;
|
||||
if (render_result) {
|
||||
size = int2(render_result->rectx, render_result->recty);
|
||||
}
|
||||
else {
|
||||
BKE_render_resolution(input_data_.render_data, true, &size.x, &size.y);
|
||||
}
|
||||
|
||||
RE_ReleaseResult(render);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
compositor::Domain get_compositing_domain() const override
|
||||
{
|
||||
return compositor::Domain(this->get_render_size());
|
||||
}
|
||||
|
||||
void write_output(const compositor::Result &result)
|
||||
{
|
||||
Render *render = RE_GetSceneRender(input_data_.scene);
|
||||
RenderResult *render_result = RE_AcquireResultWrite(render);
|
||||
|
||||
if (render_result) {
|
||||
RenderView *render_view = RE_RenderViewGetByName(render_result,
|
||||
input_data_.view_name.c_str());
|
||||
ImBuf *image_buffer = RE_RenderViewEnsureImBuf(render_result, render_view);
|
||||
render_result->have_combined = true;
|
||||
|
||||
if (result.is_single_value()) {
|
||||
float *data = MEM_new_array_uninitialized<float>(
|
||||
4 * size_t(render_result->rectx) * size_t(render_result->recty), __func__);
|
||||
image_buffer->assign_float_data(data);
|
||||
IMB_rectfill(image_buffer, result.get_single_value<compositor::Color>());
|
||||
}
|
||||
else if (this->use_gpu()) {
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
|
||||
float *output_buffer = static_cast<float *>(GPU_texture_read(result, GPU_DATA_FLOAT, 0));
|
||||
image_buffer->assign_float_data(output_buffer);
|
||||
}
|
||||
else {
|
||||
if (result.sharing_info()) {
|
||||
image_buffer->float_buffer = ImBufFloatBuffer{
|
||||
.data = static_cast<const float *>(result.cpu_data().data()),
|
||||
.sharing_info = result.sharing_info(),
|
||||
.colorspace = nullptr};
|
||||
}
|
||||
else {
|
||||
float *data = MEM_new_array_uninitialized<float>(
|
||||
4 * size_t(render_result->rectx) * size_t(render_result->recty), __func__);
|
||||
image_buffer->assign_float_data(data);
|
||||
std::memcpy(image_buffer->float_data_for_write(),
|
||||
result.cpu_data().data(),
|
||||
render_result->rectx * render_result->recty * 4 * sizeof(float));
|
||||
}
|
||||
}
|
||||
}
|
||||
RE_ReleaseResult(render);
|
||||
|
||||
Image *image = BKE_image_ensure_viewer(G.main, IMA_TYPE_R_RESULT, "Render Result");
|
||||
BKE_image_partial_update_mark_full_update(image);
|
||||
BLI_thread_lock(LOCK_DRAW_IMAGE);
|
||||
BKE_image_signal(G.main, image, nullptr, IMA_SIGNAL_FREE);
|
||||
BLI_thread_unlock(LOCK_DRAW_IMAGE);
|
||||
}
|
||||
|
||||
void write_viewer_image(const compositor::Result &viewer_result)
|
||||
{
|
||||
Image *image = BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node");
|
||||
|
||||
if (viewer_result.meta_data.is_non_color_data) {
|
||||
image->flag &= ~IMA_VIEW_AS_RENDER;
|
||||
}
|
||||
else {
|
||||
image->flag |= IMA_VIEW_AS_RENDER;
|
||||
}
|
||||
|
||||
ImageUser image_user = {nullptr};
|
||||
image_user.multi_index = BKE_scene_multiview_view_id_get(input_data_.render_data,
|
||||
input_data_.view_name.c_str());
|
||||
|
||||
if (BKE_scene_multiview_is_render_view_first(input_data_.render_data,
|
||||
input_data_.view_name.c_str()))
|
||||
{
|
||||
BKE_image_ensure_viewer_views(input_data_.render_data, image, &image_user);
|
||||
}
|
||||
|
||||
BLI_thread_lock(LOCK_DRAW_IMAGE);
|
||||
|
||||
void *lock;
|
||||
ImBuf *image_buffer = BKE_image_acquire_ibuf_gpu(image, &image_user, &lock);
|
||||
|
||||
const int2 size = viewer_result.is_single_value() ? this->get_render_size() :
|
||||
viewer_result.domain().data_size;
|
||||
|
||||
/* The image buffer has a different size than the viewer result, set the new size and free all
|
||||
* data to be reallocated later. */
|
||||
if (int2(image_buffer->x, image_buffer->y) != size) {
|
||||
IMB_free_byte_pixels(image_buffer);
|
||||
IMB_free_float_pixels(image_buffer);
|
||||
IMB_free_gpu_textures(image_buffer);
|
||||
image_buffer->x = size.x;
|
||||
image_buffer->y = size.y;
|
||||
}
|
||||
|
||||
if (this->use_gpu()) {
|
||||
/* If using GPU, free any potential previous CPU data. */
|
||||
IMB_free_float_pixels(image_buffer);
|
||||
|
||||
/* Allocate a GPU texture if using GPU and no texture exists or one exists but with a
|
||||
* different format. */
|
||||
if (!image_buffer->gpu.texture ||
|
||||
GPU_texture_format(image_buffer->gpu.texture) != viewer_result.get_gpu_texture_format())
|
||||
{
|
||||
gpu::TextureFormat format = viewer_result.get_gpu_texture_format();
|
||||
gpu::Texture *texture = GPU_texture_create_2d(
|
||||
__func__, size.x, size.y, 1, format, GPU_TEXTURE_USAGE_GENERAL, nullptr);
|
||||
IMB_assign_gpu_texture(image_buffer, texture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* If not using GPU, free any potential previous GPU data. */
|
||||
IMB_free_gpu_textures(image_buffer);
|
||||
}
|
||||
|
||||
if (this->use_gpu()) {
|
||||
if (viewer_result.is_single_value()) {
|
||||
GPU_texture_clear(image_buffer->gpu.texture,
|
||||
GPU_DATA_FLOAT,
|
||||
viewer_result.get_single_value<compositor::Color>());
|
||||
}
|
||||
else {
|
||||
GPU_texture_copy(image_buffer->gpu.texture, viewer_result);
|
||||
}
|
||||
image_buffer->userflags |= IB_HOST_BUFFER_INVALID;
|
||||
}
|
||||
else {
|
||||
if (viewer_result.is_single_value()) {
|
||||
IMB_alloc_float_pixels(image_buffer, 4, false);
|
||||
IMB_rectfill(image_buffer, viewer_result.get_single_value<compositor::Color>());
|
||||
}
|
||||
else if (viewer_result.sharing_info()) {
|
||||
image_buffer->channels = 4;
|
||||
image_buffer->float_buffer = ImBufFloatBuffer{
|
||||
.data = static_cast<const float *>(viewer_result.cpu_data().data()),
|
||||
.sharing_info = viewer_result.sharing_info(),
|
||||
.colorspace = nullptr};
|
||||
}
|
||||
else if (viewer_result.cpu_data().data() != image_buffer->float_data()) {
|
||||
IMB_alloc_float_pixels(image_buffer, 4, false);
|
||||
std::memcpy(image_buffer->float_data_for_write(),
|
||||
viewer_result.cpu_data().data(),
|
||||
size.x * size.y * 4 * sizeof(float));
|
||||
}
|
||||
image_buffer->userflags |= IB_DISPLAY_BUFFER_INVALID;
|
||||
}
|
||||
|
||||
if (!viewer_result.is_single_value()) {
|
||||
image_buffer->flags |= ImBufFlags::HasDisplayWindow;
|
||||
const int2 display_offset = int2(viewer_result.domain().transformation.location());
|
||||
copy_v2_v2_int(image_buffer->display_size, viewer_result.domain().display_size);
|
||||
copy_v2_v2_int(image_buffer->display_offset, display_offset);
|
||||
copy_v2_v2_int(image_buffer->data_offset, viewer_result.domain().data_offset);
|
||||
}
|
||||
else {
|
||||
image_buffer->flags &= ~ImBufFlags::HasDisplayWindow;
|
||||
}
|
||||
|
||||
BKE_image_partial_update_mark_full_update(image);
|
||||
BKE_image_release_ibuf(image, image_buffer, lock);
|
||||
BLI_thread_unlock(LOCK_DRAW_IMAGE);
|
||||
}
|
||||
|
||||
void write_viewer(compositor::Result &viewer_result) override
|
||||
{
|
||||
using namespace compositor;
|
||||
|
||||
/* Realize the transforms if needed. */
|
||||
const InputDescriptor input_descriptor = {ResultType::Color,
|
||||
InputRealizationMode::OperationDomain};
|
||||
SimpleOperation *realization_operation = RealizeOnDomainOperation::construct_if_needed(
|
||||
*this, viewer_result, input_descriptor, viewer_result.domain());
|
||||
|
||||
if (realization_operation) {
|
||||
Result realize_input = this->create_result(ResultType::Color, viewer_result.precision());
|
||||
realize_input.share_data(viewer_result);
|
||||
realization_operation->map_input_to_result(&realize_input);
|
||||
realization_operation->evaluate();
|
||||
|
||||
Result &realized_viewer_result = realization_operation->get_result();
|
||||
this->write_viewer_image(realized_viewer_result);
|
||||
realized_viewer_result.release();
|
||||
delete realization_operation;
|
||||
return;
|
||||
}
|
||||
|
||||
this->write_viewer_image(viewer_result);
|
||||
}
|
||||
|
||||
compositor::ResultType get_pass_data_type(const RenderPass *pass)
|
||||
{
|
||||
switch (pass->channels) {
|
||||
case 1:
|
||||
return compositor::ResultType::Float;
|
||||
case 2:
|
||||
return compositor::ResultType::Float2;
|
||||
case 3:
|
||||
return compositor::ResultType::Float3;
|
||||
case 4:
|
||||
if (StringRef(pass->chan_id) == "XYZW") {
|
||||
return compositor::ResultType::Float4;
|
||||
}
|
||||
else {
|
||||
return compositor::ResultType::Color;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return compositor::ResultType::Float;
|
||||
}
|
||||
|
||||
compositor::ResultType get_pass_type(const RenderPass *pass)
|
||||
{
|
||||
switch (pass->channels) {
|
||||
case 1:
|
||||
return compositor::ResultType::Float;
|
||||
case 2:
|
||||
return compositor::ResultType::Float2;
|
||||
case 3:
|
||||
if (StringRef(pass->chan_id) == "RGB") {
|
||||
return compositor::ResultType::Color;
|
||||
}
|
||||
else {
|
||||
return compositor::ResultType::Float3;
|
||||
}
|
||||
case 4:
|
||||
if (StringRef(pass->chan_id) == "XYZW") {
|
||||
return compositor::ResultType::Float4;
|
||||
}
|
||||
else {
|
||||
return compositor::ResultType::Color;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return compositor::ResultType::Float;
|
||||
}
|
||||
|
||||
compositor::Result get_invalid_pass()
|
||||
{
|
||||
compositor::Result invalid_pass = this->create_result(compositor::ResultType::Color);
|
||||
invalid_pass.allocate_invalid();
|
||||
return invalid_pass;
|
||||
}
|
||||
|
||||
compositor::Result get_pass(const Scene *scene, int view_layer_id, const char *name) override
|
||||
{
|
||||
/* Blender aliases the Image pass name to be the Combined pass, so we return the combined pass
|
||||
* in that case. */
|
||||
const char *pass_name = StringRef(name) == "Image" ? "Combined" : name;
|
||||
|
||||
if (!scene) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
ViewLayer *view_layer = static_cast<ViewLayer *>(
|
||||
BLI_findlink(&scene->view_layers, view_layer_id));
|
||||
if (!view_layer) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
Render *render = RE_GetSceneRender(scene);
|
||||
if (!render) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
BLI_SCOPED_DEFER([&]() { RE_ReleaseResult(render); });
|
||||
|
||||
RenderResult *render_result = RE_AcquireResultRead(render);
|
||||
if (!render_result) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name);
|
||||
if (!render_layer) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
RenderPass *render_pass = RE_pass_find_by_name(
|
||||
render_layer, pass_name, this->get_view_name().data());
|
||||
if (!render_pass) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
if (!render_pass || !render_pass->ibuf || !render_pass->ibuf->float_data()) {
|
||||
return this->get_invalid_pass();
|
||||
}
|
||||
|
||||
compositor::Result pass_data = compositor::Result(
|
||||
*this, this->get_pass_data_type(render_pass), compositor::ResultPrecision::Full);
|
||||
|
||||
if (this->use_gpu()) {
|
||||
gpu::Texture *pass_texture = RE_pass_ensure_gpu_texture_cache(render, render_pass);
|
||||
/* Don't assume render will keep pass data stored, add our own reference. */
|
||||
GPU_texture_ref(pass_texture);
|
||||
pass_data.share_data(pass_texture);
|
||||
cached_gpu_passes_.append(pass_texture);
|
||||
}
|
||||
else {
|
||||
/* Don't assume render will keep pass data stored, add our own reference. */
|
||||
IMB_refImBuf(render_pass->ibuf);
|
||||
pass_data.share_data(render_pass->ibuf->float_buffer.data,
|
||||
int2(render_pass->ibuf->x, render_pass->ibuf->y),
|
||||
render_pass->ibuf->float_buffer.sharing_info);
|
||||
cached_cpu_passes_.append(render_pass->ibuf);
|
||||
}
|
||||
|
||||
compositor::Result pass = compositor::Result(
|
||||
*this, this->get_pass_type(render_pass), compositor::ResultPrecision::Full);
|
||||
if (pass.type() != pass_data.type()) {
|
||||
compositor::ConversionOperation conversion_operation(*this, pass_data.type(), pass.type());
|
||||
conversion_operation.map_input_to_result(&pass_data);
|
||||
conversion_operation.evaluate();
|
||||
pass.share_data(conversion_operation.get_result());
|
||||
conversion_operation.get_result().release();
|
||||
}
|
||||
else {
|
||||
pass.share_data(pass_data);
|
||||
pass_data.release();
|
||||
}
|
||||
|
||||
/* We assume the given pass is a Cryptomatte pass and retrieve its layer name. If it wasn't a
|
||||
* Cryptomatte pass, the checks below will fail anyway. */
|
||||
const std::string combined_pass_name = std::string(view_layer->name) + "." + pass_name;
|
||||
StringRef cryptomatte_layer_name = bke::cryptomatte::BKE_cryptomatte_extract_layer_name(
|
||||
combined_pass_name);
|
||||
|
||||
struct StampCallbackData {
|
||||
std::string cryptomatte_layer_name;
|
||||
compositor::MetaData *meta_data;
|
||||
};
|
||||
|
||||
/* Go over the stamp data and add any Cryptomatte related meta data. */
|
||||
StampCallbackData callback_data = {cryptomatte_layer_name, &pass.meta_data};
|
||||
BKE_stamp_info_callback(
|
||||
&callback_data,
|
||||
render_result->stamp_data,
|
||||
[](void *user_data, const char *key, char *value, int /*value_maxncpy*/) {
|
||||
StampCallbackData *data = static_cast<StampCallbackData *>(user_data);
|
||||
|
||||
const std::string manifest_key = bke::cryptomatte::BKE_cryptomatte_meta_data_key(
|
||||
data->cryptomatte_layer_name, "manifest");
|
||||
if (key == manifest_key) {
|
||||
data->meta_data->cryptomatte.manifest = value;
|
||||
}
|
||||
|
||||
const std::string hash_key = bke::cryptomatte::BKE_cryptomatte_meta_data_key(
|
||||
data->cryptomatte_layer_name, "hash");
|
||||
if (key == hash_key) {
|
||||
data->meta_data->cryptomatte.hash = value;
|
||||
}
|
||||
|
||||
const std::string conversion_key = bke::cryptomatte::BKE_cryptomatte_meta_data_key(
|
||||
data->cryptomatte_layer_name, "conversion");
|
||||
if (key == conversion_key) {
|
||||
data->meta_data->cryptomatte.conversion = value;
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
StringRef get_view_name() const override
|
||||
{
|
||||
return input_data_.view_name;
|
||||
}
|
||||
|
||||
compositor::ResultPrecision get_precision() const override
|
||||
{
|
||||
switch (input_data_.scene->r.compositor_precision) {
|
||||
case SCE_COMPOSITOR_PRECISION_AUTO:
|
||||
/* Auto uses full precision for final renders and half precision otherwise. */
|
||||
if (this->render_context()) {
|
||||
return compositor::ResultPrecision::Full;
|
||||
}
|
||||
else {
|
||||
return compositor::ResultPrecision::Half;
|
||||
}
|
||||
case SCE_COMPOSITOR_PRECISION_FULL:
|
||||
return compositor::ResultPrecision::Full;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return compositor::ResultPrecision::Full;
|
||||
}
|
||||
|
||||
compositor::RenderContext *render_context() const override
|
||||
{
|
||||
return input_data_.render_context;
|
||||
}
|
||||
|
||||
nodes::eval_log::NodesEvalLog *nodes_evaluation_log() const override
|
||||
{
|
||||
return this->get_scene().runtime->compositor.nodes_evaluation_log.get();
|
||||
}
|
||||
|
||||
void evaluate_operation_post() const override
|
||||
{
|
||||
/* If no render context exist, that means this is an interactive compositor evaluation due to
|
||||
* the user editing the node tree. In that case, we wait until the operation finishes executing
|
||||
* on the GPU before we continue to improve interactivity. The improvement comes from the fact
|
||||
* that the user might be rapidly changing values, so we need to cancel previous evaluations to
|
||||
* make editing faster, but we can't do that if all operations are submitted to the GPU all at
|
||||
* once, and we can't cancel work that was already submitted to the GPU. This does have a
|
||||
* performance penalty, but in practice, the improved interactivity is worth it according to
|
||||
* user feedback. */
|
||||
if (this->use_gpu() && !this->render_context()) {
|
||||
GPU_finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_canceled() const override
|
||||
{
|
||||
return input_data_.render->display->test_break();
|
||||
}
|
||||
|
||||
void evaluate()
|
||||
{
|
||||
/* Reset log before evaluation. */
|
||||
this->get_scene().runtime->compositor.nodes_evaluation_log =
|
||||
std::make_unique<nodes::eval_log::NodesEvalLog>();
|
||||
|
||||
using namespace compositor;
|
||||
const NodeGroupOutputTypes needed_outputs = this->needed_outputs();
|
||||
const bNodeTree &node_group = *input_data_.node_tree;
|
||||
const bke::DataBlockComputeContext compute_context(nullptr, this->get_scene().id);
|
||||
NodeGroupOperation node_group_operation(*this,
|
||||
node_group,
|
||||
needed_outputs,
|
||||
node_group.active_viewer_key,
|
||||
bke::NODE_INSTANCE_KEY_BASE,
|
||||
compute_context);
|
||||
|
||||
/* Set the reference count for the outputs, only the first color output is actually needed,
|
||||
* while the rest are ignored. */
|
||||
const bool is_group_output_needed = flag_is_set(needed_outputs,
|
||||
NodeGroupOutputTypes::GroupOutputNode);
|
||||
node_group.ensure_interface_cache();
|
||||
for (const bNodeTreeInterfaceSocket *output_socket : node_group.interface_outputs()) {
|
||||
const bool is_first_output = output_socket == node_group.interface_outputs().first();
|
||||
Result &output_result = node_group_operation.get_result(output_socket->identifier);
|
||||
const bool is_color = output_result.type() == ResultType::Color;
|
||||
const bool is_needed = is_group_output_needed && is_first_output && is_color;
|
||||
output_result.set_reference_count(is_needed ? 1 : 0);
|
||||
}
|
||||
|
||||
/* Map the inputs to the operation. */
|
||||
Vector<std::unique_ptr<Result>> inputs;
|
||||
for (const bNodeTreeInterfaceSocket *input_socket : node_group.interface_inputs()) {
|
||||
Result *input_result = new Result(
|
||||
this->create_result(ResultType::Color, ResultPrecision::Full));
|
||||
if (input_socket == node_group.interface_inputs()[0]) {
|
||||
/* First socket is the combined pass. */
|
||||
Result combined_pass = this->get_pass(&this->get_scene(), 0, "Image");
|
||||
if (combined_pass.is_allocated()) {
|
||||
input_result->share_data(combined_pass);
|
||||
}
|
||||
else {
|
||||
input_result->allocate_invalid();
|
||||
}
|
||||
combined_pass.release();
|
||||
}
|
||||
else {
|
||||
/* The rest of the sockets are not supported. */
|
||||
input_result->allocate_invalid();
|
||||
}
|
||||
|
||||
node_group_operation.map_input_to_result(input_socket->identifier, input_result);
|
||||
inputs.append(std::unique_ptr<Result>(input_result));
|
||||
}
|
||||
|
||||
node_group_operation.evaluate();
|
||||
|
||||
/* Write the outputs of the operation. */
|
||||
for (const bNodeTreeInterfaceSocket *output_socket : node_group.interface_outputs()) {
|
||||
Result &output_result = node_group_operation.get_result(output_socket->identifier);
|
||||
if (!output_result.should_compute()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this->is_canceled()) {
|
||||
output_result.release();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Realize the output on the compositing domain if needed. */
|
||||
const Domain compositing_domain = this->get_compositing_domain();
|
||||
const InputDescriptor input_descriptor = {ResultType::Color,
|
||||
InputRealizationMode::OperationDomain};
|
||||
SimpleOperation *realization_operation = RealizeOnDomainOperation::construct_if_needed(
|
||||
*this, output_result, input_descriptor, compositing_domain);
|
||||
if (realization_operation) {
|
||||
realization_operation->map_input_to_result(&output_result);
|
||||
realization_operation->evaluate();
|
||||
Result &realized_output_result = realization_operation->get_result();
|
||||
this->write_output(realized_output_result);
|
||||
realized_output_result.release();
|
||||
delete realization_operation;
|
||||
continue;
|
||||
}
|
||||
|
||||
this->write_output(output_result);
|
||||
output_result.release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Render Compositor */
|
||||
|
||||
class Compositor {
|
||||
private:
|
||||
/* Render instance for GPU context to run compositor in. */
|
||||
Render &render_;
|
||||
|
||||
compositor::StaticCacheManager cache_manager_;
|
||||
|
||||
/* Stores the execution device and precision used in the last evaluation of the compositor. Those
|
||||
* might be different from the current values returned by the context, since the user might have
|
||||
* changed them since the last evaluation. See the needs_to_be_recreated method for more info on
|
||||
* why those are needed. */
|
||||
bool last_evaluation_used_gpu_ = false;
|
||||
compositor::ResultPrecision last_evaluation_precision_ = compositor::ResultPrecision::Half;
|
||||
|
||||
public:
|
||||
Compositor(Render &render) : render_(render) {}
|
||||
|
||||
~Compositor()
|
||||
{
|
||||
/* Use last_evaluation_used_gpu_ instead of the currently used device because we are freeing
|
||||
* resources from the last evaluation. See last_evaluation_used_gpu_ for more information. */
|
||||
if (last_evaluation_used_gpu_) {
|
||||
/* Free resources with GPU context enabled. Cleanup may happen from the main thread, and we
|
||||
* must use the main context there. */
|
||||
if (BLI_thread_is_main()) {
|
||||
DRW_gpu_context_enable();
|
||||
}
|
||||
else {
|
||||
DRW_render_context_enable(&render_);
|
||||
}
|
||||
}
|
||||
|
||||
cache_manager_.free();
|
||||
|
||||
/* See comment above on context enabling. */
|
||||
if (last_evaluation_used_gpu_) {
|
||||
if (BLI_thread_is_main()) {
|
||||
DRW_gpu_context_disable();
|
||||
}
|
||||
else {
|
||||
DRW_render_context_disable(&render_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void execute(const ContextInputData &input_data)
|
||||
{
|
||||
Context context(cache_manager_, input_data);
|
||||
|
||||
if (context.use_gpu()) {
|
||||
/* For main thread rendering in background mode, blocking rendering, or when we do not have a
|
||||
* render system GPU context, use the DRW context directly, while for threaded rendering when
|
||||
* we have a render system GPU context, use the render's system GPU context to avoid blocking
|
||||
* with the global DST. */
|
||||
GHOST_IContext *re_system_gpu_context = RE_system_gpu_context_get(&render_);
|
||||
if (BLI_thread_is_main() || re_system_gpu_context == nullptr) {
|
||||
DRW_gpu_context_enable();
|
||||
context.set_gpu_supported(DRW_gpu_context_is_enabled());
|
||||
}
|
||||
else if (re_system_gpu_context) {
|
||||
WM_system_gpu_context_activate(re_system_gpu_context);
|
||||
|
||||
void *re_blender_gpu_context = RE_blender_gpu_context_ensure(&render_);
|
||||
|
||||
GPU_render_begin();
|
||||
GPU_context_active_set(static_cast<GPUContext *>(re_blender_gpu_context));
|
||||
}
|
||||
else {
|
||||
context.set_gpu_supported(false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
context.evaluate();
|
||||
|
||||
/* Reset the cache, but only if the evaluation did not get canceled, because in that case, we
|
||||
* wouldn't want to invalidate the cache because not all operations that use cached resources
|
||||
* got the chance to mark their used resources as still in use. So we wait until a full
|
||||
* evaluation happen before we decide that some resources are no longer needed. */
|
||||
if (!context.is_canceled()) {
|
||||
context.cache_manager().reset();
|
||||
}
|
||||
|
||||
last_evaluation_used_gpu_ = context.use_gpu();
|
||||
last_evaluation_precision_ = context.get_precision();
|
||||
}
|
||||
|
||||
if (context.use_gpu()) {
|
||||
gpu::TexturePool::get().reset();
|
||||
|
||||
GHOST_IContext *re_system_gpu_context = RE_system_gpu_context_get(&render_);
|
||||
if (BLI_thread_is_main() || re_system_gpu_context == nullptr) {
|
||||
DRW_gpu_context_disable();
|
||||
}
|
||||
else {
|
||||
GPU_context_active_set(nullptr);
|
||||
GPU_render_end();
|
||||
GHOST_IContext *re_system_gpu_context = RE_system_gpu_context_get(&render_);
|
||||
WM_system_gpu_context_release(re_system_gpu_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if the compositor should be freed and reconstructed, which is needed when the
|
||||
* compositor execution device or precision changed, because we either need to update all cached
|
||||
* resources for the new execution device and precision, or we simply recreate the entire
|
||||
* compositor, since it is much easier and safer. */
|
||||
bool needs_to_be_recreated(const ContextInputData &input_data)
|
||||
{
|
||||
Context context(cache_manager_, input_data);
|
||||
/* See last_evaluation_used_gpu_ and last_evaluation_precision_ for more information what how
|
||||
* they are different from the ones returned from the context. */
|
||||
return context.use_gpu() != last_evaluation_used_gpu_ ||
|
||||
context.get_precision() != last_evaluation_precision_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace render
|
||||
|
||||
void Render::compositor_execute(const Main &main,
|
||||
const Scene &scene,
|
||||
const RenderData &render_data,
|
||||
const bNodeTree &node_tree,
|
||||
const char *view_name,
|
||||
compositor::RenderContext *render_context,
|
||||
compositor::NodeGroupOutputTypes needed_outputs)
|
||||
{
|
||||
std::unique_lock lock(this->compositor_mutex);
|
||||
|
||||
render::ContextInputData input_data(
|
||||
this, main, scene, render_data, node_tree, view_name, render_context, needed_outputs);
|
||||
|
||||
if (this->compositor && this->compositor->needs_to_be_recreated(input_data)) {
|
||||
/* Free it here and it will be recreated in the check below. */
|
||||
delete this->compositor;
|
||||
this->compositor = nullptr;
|
||||
}
|
||||
|
||||
if (!this->compositor) {
|
||||
this->compositor = new render::Compositor(*this);
|
||||
}
|
||||
|
||||
this->compositor->execute(input_data);
|
||||
}
|
||||
|
||||
void Render::compositor_free()
|
||||
{
|
||||
std::unique_lock lock(this->compositor_mutex);
|
||||
|
||||
if (this->compositor != nullptr) {
|
||||
delete this->compositor;
|
||||
this->compositor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RE_compositor_execute(Render &render,
|
||||
const Main &main,
|
||||
const Scene &scene,
|
||||
const RenderData &render_data,
|
||||
const bNodeTree &node_tree,
|
||||
const char *view_name,
|
||||
compositor::RenderContext *render_context,
|
||||
compositor::NodeGroupOutputTypes needed_outputs)
|
||||
{
|
||||
render.compositor_execute(
|
||||
main, scene, render_data, node_tree, view_name, render_context, needed_outputs);
|
||||
}
|
||||
|
||||
void RE_compositor_free(Render &render)
|
||||
{
|
||||
render.compositor_free();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1449
blender-5.2.0/source/blender/render/intern/engine.cc
Normal file
1449
blender-5.2.0/source/blender/render/intern/engine.cc
Normal file
File diff suppressed because it is too large
Load Diff
115
blender-5.2.0/source/blender/render/intern/initrender.cc
Normal file
115
blender-5.2.0/source/blender/render/intern/initrender.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
/* Global includes */
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_camera.h"
|
||||
|
||||
/* this module */
|
||||
#include "RE_pipeline.h"
|
||||
#include "render_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||
|
||||
Object *RE_GetCamera(Render *re)
|
||||
{
|
||||
Object *camera = re->camera_override ? re->camera_override : re->scene->camera;
|
||||
return BKE_camera_multiview_render(*re->main, re->scene, camera, re->viewname);
|
||||
}
|
||||
|
||||
void RE_SetOverrideCamera(Render *re, Object *cam_ob)
|
||||
{
|
||||
re->camera_override = cam_ob;
|
||||
}
|
||||
|
||||
void RE_SetCamera(Render *re, const Object *cam_ob)
|
||||
{
|
||||
CameraParams params;
|
||||
|
||||
/* setup parameters */
|
||||
BKE_camera_params_init(¶ms);
|
||||
BKE_camera_params_from_object(¶ms, cam_ob);
|
||||
BKE_camera_multiview_params(&re->r, ¶ms, cam_ob, re->viewname);
|
||||
|
||||
/* Compute matrix, view-plane, etc. */
|
||||
BKE_camera_params_compute_viewplane(¶ms, re->winx, re->winy, re->r.xasp, re->r.yasp);
|
||||
BKE_camera_params_compute_matrix(¶ms);
|
||||
|
||||
/* extract results */
|
||||
copy_m4_m4(re->winmat, params.winmat);
|
||||
re->clip_start = params.clip_start;
|
||||
re->clip_end = params.clip_end;
|
||||
re->viewplane = params.viewplane;
|
||||
}
|
||||
|
||||
void RE_GetCameraWindow(Render *re, const Object *camera, float r_winmat[4][4])
|
||||
{
|
||||
RE_SetCamera(re, camera);
|
||||
copy_m4_m4(r_winmat, re->winmat);
|
||||
}
|
||||
|
||||
void RE_GetCameraWindowWithOverscan(const Render *re, float overscan, float r_winmat[4][4])
|
||||
{
|
||||
RE_GetWindowMatrixWithOverscan(
|
||||
re->winmat[3][3] != 0.0f, re->clip_start, re->clip_end, re->viewplane, overscan, r_winmat);
|
||||
}
|
||||
|
||||
void RE_GetCameraModelMatrix(const Render *re, const Object *camera, float r_modelmat[4][4])
|
||||
{
|
||||
BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_modelmat);
|
||||
}
|
||||
|
||||
void RE_GetWindowMatrixWithOverscan(bool is_ortho,
|
||||
float clip_start,
|
||||
float clip_end,
|
||||
rctf viewplane,
|
||||
float overscan,
|
||||
float r_winmat[4][4])
|
||||
{
|
||||
CameraParams params;
|
||||
params.is_ortho = is_ortho;
|
||||
params.clip_start = clip_start;
|
||||
params.clip_end = clip_end;
|
||||
params.viewplane = viewplane;
|
||||
|
||||
overscan *= max_ff(BLI_rctf_size_x(¶ms.viewplane), BLI_rctf_size_y(¶ms.viewplane));
|
||||
|
||||
params.viewplane.xmin -= overscan;
|
||||
params.viewplane.xmax += overscan;
|
||||
params.viewplane.ymin -= overscan;
|
||||
params.viewplane.ymax += overscan;
|
||||
BKE_camera_params_compute_matrix(¶ms);
|
||||
copy_m4_m4(r_winmat, params.winmat);
|
||||
}
|
||||
|
||||
void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect)
|
||||
{
|
||||
*r_viewplane = re->viewplane;
|
||||
|
||||
/* make disprect zero when no border render, is needed to detect changes in 3d view render */
|
||||
if (re->r.mode & R_BORDER) {
|
||||
*r_disprect = re->disprect;
|
||||
}
|
||||
else {
|
||||
BLI_rcti_init(r_disprect, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1734
blender-5.2.0/source/blender/render/intern/multires_bake.cc
Normal file
1734
blender-5.2.0/source/blender/render/intern/multires_bake.cc
Normal file
File diff suppressed because it is too large
Load Diff
2849
blender-5.2.0/source/blender/render/intern/pipeline.cc
Normal file
2849
blender-5.2.0/source/blender/render/intern/pipeline.cc
Normal file
File diff suppressed because it is too large
Load Diff
21
blender-5.2.0/source/blender/render/intern/pipeline.hh
Normal file
21
blender-5.2.0/source/blender/render/intern/pipeline.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Render;
|
||||
struct RenderData;
|
||||
struct RenderLayer;
|
||||
struct RenderResult;
|
||||
|
||||
RenderLayer *render_get_single_layer(Render *re, RenderResult *rr);
|
||||
void render_copy_renderdata(RenderData *to, RenderData *from);
|
||||
|
||||
} // namespace blender
|
||||
1362
blender-5.2.0/source/blender/render/intern/render_result.cc
Normal file
1362
blender-5.2.0/source/blender/render/intern/render_result.cc
Normal file
File diff suppressed because it is too large
Load Diff
163
blender-5.2.0/source/blender/render/intern/render_result.h
Normal file
163
blender-5.2.0/source/blender/render/intern/render_result.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/* SPDX-FileCopyrightText: 2007 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BKE_global.hh" /* IWYU pragma: keep. Used in macro. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define PASS_VECTOR_MAX 10000.0f
|
||||
|
||||
#define RR_ALL_LAYERS NULL
|
||||
#define RR_ALL_VIEWS NULL
|
||||
|
||||
struct ColorManagedDisplaySettings;
|
||||
struct ColorManagedViewSettings;
|
||||
struct ExrHandle;
|
||||
struct ImBuf;
|
||||
struct Render;
|
||||
struct RenderData;
|
||||
struct RenderLayer;
|
||||
struct RenderResult;
|
||||
struct ReportList;
|
||||
struct rcti;
|
||||
|
||||
/* New */
|
||||
|
||||
/**
|
||||
* Called by main render as well for parts will read info from Render *re to define layers.
|
||||
* \note Called in threads.
|
||||
*
|
||||
* `re->winx`, `re->winy` is coordinate space of entire image, `partrct` the part within.
|
||||
*/
|
||||
struct RenderResult *render_result_new(struct Render *re,
|
||||
const struct rcti *partrct,
|
||||
const char *layername,
|
||||
const char *viewname);
|
||||
|
||||
void render_result_passes_allocated_ensure(struct RenderResult *rr);
|
||||
|
||||
/**
|
||||
* From `imbuf`, if a handle was returned and
|
||||
* it's not a single-layer multi-view we convert this to render result.
|
||||
*/
|
||||
struct RenderResult *render_result_new_from_exr(
|
||||
ExrHandle *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
|
||||
|
||||
void render_result_view_new(struct RenderResult *rr, const char *viewname);
|
||||
void render_result_views_new(struct RenderResult *rr, const struct RenderData *rd);
|
||||
|
||||
/* Merge */
|
||||
|
||||
/**
|
||||
* Used when rendering to a full buffer, or when reading the EXR part-layer-pass file.
|
||||
* no test happens here if it fits... we also assume layers are in sync.
|
||||
* \note Is used within threads.
|
||||
*/
|
||||
void render_result_merge(struct RenderResult *rr, struct RenderResult *rrpart);
|
||||
|
||||
/* Add Passes */
|
||||
|
||||
void render_result_clone_passes(struct Render *re, struct RenderResult *rr, const char *viewname);
|
||||
|
||||
/* Free */
|
||||
|
||||
void render_result_free(struct RenderResult *rr);
|
||||
/**
|
||||
* Version that's compatible with full-sample buffers.
|
||||
*/
|
||||
void render_result_free_list(ListBaseT<RenderResult> *lb, struct RenderResult *rr);
|
||||
|
||||
/* Single Layer Render */
|
||||
|
||||
void render_result_single_layer_begin(struct Render *re);
|
||||
/**
|
||||
* If #RenderData.scemode is #R_SINGLE_LAYER, at end of rendering, merge the both render results.
|
||||
*/
|
||||
void render_result_single_layer_end(struct Render *re);
|
||||
|
||||
/**
|
||||
* Render pass wrapper for grease-pencil.
|
||||
*/
|
||||
struct RenderPass *render_layer_add_pass(struct RenderResult *rr,
|
||||
struct RenderLayer *rl,
|
||||
int channels,
|
||||
const char *name,
|
||||
const char *viewname,
|
||||
const char *chan_id,
|
||||
bool allocate);
|
||||
|
||||
/**
|
||||
* Called for reading temp files, and for external engines.
|
||||
*/
|
||||
bool render_result_exr_file_read_path(struct RenderResult *rr,
|
||||
struct RenderLayer *rl_single,
|
||||
struct ReportList *reports,
|
||||
const char *filepath);
|
||||
|
||||
/* EXR cache */
|
||||
|
||||
void render_result_exr_file_cache_write(struct Render *re);
|
||||
/**
|
||||
* For cache, makes exact copy of render result.
|
||||
*/
|
||||
bool render_result_exr_file_cache_read(struct Render *re);
|
||||
|
||||
/* Combined Pixel Rect */
|
||||
|
||||
struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr,
|
||||
const struct RenderData *rd,
|
||||
int view_id);
|
||||
|
||||
void render_result_rect_fill_zero(struct RenderResult *rr, int view_id);
|
||||
void render_result_rect_get_pixels(struct RenderResult *rr,
|
||||
uint8_t *rect,
|
||||
int rectx,
|
||||
int recty,
|
||||
const struct ColorManagedViewSettings *view_settings,
|
||||
const struct ColorManagedDisplaySettings *display_settings,
|
||||
int view_id);
|
||||
|
||||
/**
|
||||
* Create a new views #ListBaseT in rr without duplicating the memory pointers.
|
||||
*/
|
||||
void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src);
|
||||
/**
|
||||
* Free the views created temporarily.
|
||||
*/
|
||||
void render_result_views_shallowdelete(struct RenderResult *rr);
|
||||
|
||||
/**
|
||||
* Free GPU texture caches to reduce memory usage.
|
||||
*/
|
||||
void render_result_free_gpu_texture_caches(struct RenderResult *rr);
|
||||
|
||||
#define FOREACH_VIEW_LAYER_TO_RENDER_BEGIN(re_, iter_) \
|
||||
{ \
|
||||
ViewLayer *iter_; \
|
||||
for (iter_ = static_cast<ViewLayer *>((re_)->scene->view_layers.first); iter_ != NULL; \
|
||||
iter_ = iter_->next) \
|
||||
{ \
|
||||
if (!G.background && (re_)->r.scemode & R_SINGLE_LAYER) { \
|
||||
if (!STREQ(iter_->name, re->single_view_layer)) { \
|
||||
continue; \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
if ((iter_->flag & VIEW_LAYER_RENDER) == 0) { \
|
||||
continue; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define FOREACH_VIEW_LAYER_TO_RENDER_END \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
} // namespace blender
|
||||
185
blender-5.2.0/source/blender/render/intern/render_types.cc
Normal file
185
blender-5.2.0/source/blender/render/intern/render_types.cc
Normal file
@@ -0,0 +1,185 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include "render_types.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BKE_colortools.hh"
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "RE_compositor.hh"
|
||||
#include "RE_engine.h"
|
||||
|
||||
#include "render_result.h"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "wm_window.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Render
|
||||
* \{ */
|
||||
|
||||
BaseRender::~BaseRender()
|
||||
{
|
||||
if (engine) {
|
||||
RE_engine_free(engine);
|
||||
}
|
||||
|
||||
render_result_free(result);
|
||||
|
||||
/* Free GPU context after engine, which may need context for cleanup. */
|
||||
display.reset();
|
||||
|
||||
BLI_rw_mutex_end(&resultmutex);
|
||||
BLI_mutex_end(&engine_draw_mutex);
|
||||
}
|
||||
|
||||
Render::Render()
|
||||
{
|
||||
display = std::make_shared<RenderDisplay>();
|
||||
}
|
||||
|
||||
Render::~Render()
|
||||
{
|
||||
RE_compositor_free(*this);
|
||||
|
||||
BKE_curvemapping_free_data(&r.mblur_shutter_curve);
|
||||
|
||||
render_result_free(pushedresult);
|
||||
}
|
||||
|
||||
bool Render::prepare_viewlayer(ViewLayer *view_layer, Depsgraph *depsgraph)
|
||||
{
|
||||
if (!prepare_viewlayer_cb) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return prepare_viewlayer_cb(prepare_vl_handle, view_layer, depsgraph);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name RenderDisplay
|
||||
* \{ */
|
||||
|
||||
RenderDisplay::~RenderDisplay()
|
||||
{
|
||||
free_gpu_context();
|
||||
|
||||
display_update_cb = nullptr;
|
||||
current_scene_update_cb = nullptr;
|
||||
stats_draw_cb = nullptr;
|
||||
progress_cb = nullptr;
|
||||
draw_lock_cb = nullptr;
|
||||
test_break_cb = nullptr;
|
||||
}
|
||||
|
||||
void RenderDisplay::free_gpu_context()
|
||||
{
|
||||
if (blender_gpu_context) {
|
||||
WM_system_gpu_context_activate(system_gpu_context);
|
||||
GPU_context_active_set(static_cast<GPUContext *>(blender_gpu_context));
|
||||
GPU_context_discard(static_cast<GPUContext *>(blender_gpu_context));
|
||||
blender_gpu_context = nullptr;
|
||||
}
|
||||
|
||||
if (system_gpu_context) {
|
||||
WM_system_gpu_context_dispose(system_gpu_context);
|
||||
system_gpu_context = nullptr;
|
||||
|
||||
/* If in main thread, reset window context. */
|
||||
if (BLI_thread_is_main()) {
|
||||
wm_window_reset_drawable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDisplay::ensure_system_gpu_context()
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
if (system_gpu_context == nullptr) {
|
||||
/* Needs to be created in the main thread. */
|
||||
system_gpu_context = WM_system_gpu_context_create();
|
||||
/* The context is activated during creation, so release it here since the function should not
|
||||
* have context activation as a side effect. Then activate the drawable's context below. */
|
||||
if (system_gpu_context) {
|
||||
WM_system_gpu_context_release(system_gpu_context);
|
||||
}
|
||||
wm_window_reset_drawable();
|
||||
}
|
||||
}
|
||||
|
||||
void *RenderDisplay::ensure_blender_gpu_context()
|
||||
{
|
||||
BLI_assert(system_gpu_context != nullptr);
|
||||
if (blender_gpu_context == nullptr) {
|
||||
blender_gpu_context = GPU_context_create(nullptr, system_gpu_context);
|
||||
}
|
||||
return blender_gpu_context;
|
||||
}
|
||||
|
||||
void RenderDisplay::display_update(RenderResult *render_result, rcti *rect)
|
||||
{
|
||||
if (display_update_cb) {
|
||||
display_update_cb(duh, render_result, rect);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDisplay::current_scene_update(Scene *scene)
|
||||
{
|
||||
if (current_scene_update_cb) {
|
||||
current_scene_update_cb(suh, scene);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDisplay::stats_draw(RenderStats *render_stats)
|
||||
{
|
||||
if (stats_draw_cb) {
|
||||
stats_draw_cb(sdh, render_stats);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDisplay::progress(float progress)
|
||||
{
|
||||
if (progress_cb) {
|
||||
progress_cb(prh, progress);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderDisplay::draw_lock()
|
||||
{
|
||||
if (draw_lock_cb) {
|
||||
draw_lock_cb(dlh, true);
|
||||
}
|
||||
}
|
||||
void RenderDisplay::draw_unlock()
|
||||
{
|
||||
if (draw_lock_cb) {
|
||||
draw_lock_cb(dlh, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderDisplay::test_break()
|
||||
{
|
||||
if (!test_break_cb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return test_break_cb(tbh);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
258
blender-5.2.0/source/blender/render/intern/render_types.h
Normal file
258
blender-5.2.0/source/blender/render/intern/render_types.h
Normal file
@@ -0,0 +1,258 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* exposed internal in render module only! */
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "RE_compositor.hh"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "tile_highlight.h"
|
||||
|
||||
class GHOST_IContext;
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace compositor {
|
||||
class RenderContext;
|
||||
enum class NodeGroupOutputTypes : uint8_t;
|
||||
} // namespace compositor
|
||||
|
||||
struct bNodeTree;
|
||||
struct Depsgraph;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct RenderDisplay;
|
||||
struct RenderEngine;
|
||||
struct ReportList;
|
||||
struct Scene;
|
||||
|
||||
struct BaseRender {
|
||||
BaseRender() = default;
|
||||
virtual ~BaseRender();
|
||||
|
||||
/* Get class which manages highlight of tiles.
|
||||
* Note that it might not exist: for example, viewport render does not support the tile
|
||||
* highlight. */
|
||||
virtual render::TilesHighlight *get_tile_highlight() = 0;
|
||||
|
||||
virtual void compositor_execute(const Main &main,
|
||||
const Scene &scene,
|
||||
const RenderData &render_data,
|
||||
const bNodeTree &node_tree,
|
||||
const char *view_name,
|
||||
compositor::RenderContext *render_context,
|
||||
compositor::NodeGroupOutputTypes needed_outputs) = 0;
|
||||
virtual void compositor_free() = 0;
|
||||
|
||||
/**
|
||||
* Executed right before the initialization of the depsgraph, in order to modify some stuff in
|
||||
* the viewlayer. The modified ids must be tagged in the depsgraph.
|
||||
*
|
||||
* If false is returned then rendering is aborted,
|
||||
*/
|
||||
virtual bool prepare_viewlayer(struct ViewLayer *view_layer, struct Depsgraph *depsgraph) = 0;
|
||||
|
||||
/* Result of rendering */
|
||||
RenderResult *result = nullptr;
|
||||
|
||||
/* Read/write mutex, all internal code that writes to the `result` must use a
|
||||
* write lock, all external code must use a read lock. Internal code is assumed
|
||||
* to not conflict with writes, so no lock used for that. */
|
||||
ThreadRWMutex resultmutex = BLI_RWLOCK_INITIALIZER;
|
||||
|
||||
/* Render engine. */
|
||||
struct RenderEngine *engine = nullptr;
|
||||
|
||||
/* Guard for drawing render result using engine's `draw()` callback. */
|
||||
ThreadMutex engine_draw_mutex = BLI_MUTEX_INITIALIZER;
|
||||
|
||||
/**
|
||||
* GPU context and callbacks. This can be shared for recursive compositor and
|
||||
* sequencer renders that we want to display in the same place.
|
||||
*/
|
||||
std::shared_ptr<RenderDisplay> display;
|
||||
bool display_shared = false;
|
||||
};
|
||||
|
||||
struct ViewRender : public BaseRender {
|
||||
render::TilesHighlight *get_tile_highlight() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void compositor_execute(const Main & /*main*/,
|
||||
const Scene & /*scene*/,
|
||||
const RenderData & /*render_data*/,
|
||||
const bNodeTree & /*node_tree*/,
|
||||
const char * /*view_name*/,
|
||||
compositor::RenderContext * /*render_context*/,
|
||||
compositor::NodeGroupOutputTypes /*needed_outputs*/) override
|
||||
{
|
||||
}
|
||||
void compositor_free() override {}
|
||||
|
||||
bool prepare_viewlayer(struct ViewLayer * /*view_layer*/,
|
||||
struct Depsgraph * /*depsgraph*/) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** Controls state of render, everything that's read-only during render stage. */
|
||||
struct Render : public BaseRender {
|
||||
Render();
|
||||
~Render() override;
|
||||
|
||||
render::TilesHighlight *get_tile_highlight() override
|
||||
{
|
||||
return &tile_highlight;
|
||||
}
|
||||
|
||||
void compositor_execute(const Main &main,
|
||||
const Scene &scene,
|
||||
const RenderData &render_data,
|
||||
const bNodeTree &node_tree,
|
||||
const char *view_name,
|
||||
compositor::RenderContext *render_context,
|
||||
compositor::NodeGroupOutputTypes needed_outputs) override;
|
||||
void compositor_free() override;
|
||||
|
||||
bool prepare_viewlayer(struct ViewLayer *view_layer, struct Depsgraph *depsgraph) override;
|
||||
|
||||
/* Owner pointer that uniquely identifiers the owner of this scene. */
|
||||
const void *owner = nullptr;
|
||||
|
||||
/* state settings */
|
||||
short flag = 0;
|
||||
bool ok = false;
|
||||
|
||||
/* if render with single-layer option, other rendered layers are stored here */
|
||||
RenderResult *pushedresult = nullptr;
|
||||
/** A list of #RenderResults, for full-samples. */
|
||||
ListBaseT<RenderResult> fullresult = {nullptr, nullptr};
|
||||
/* True if result has GPU textures, to quickly skip cache clear. */
|
||||
bool result_has_gpu_texture_caches = false;
|
||||
|
||||
/** Window size, display rect, viewplane.
|
||||
* \note Buffer width and height with percentage applied
|
||||
* without border & crop. convert to long before multiplying together to avoid overflow. */
|
||||
int winx = 0, winy = 0;
|
||||
rcti disprect = {0, 0, 0, 0}; /* part within winx winy */
|
||||
rctf viewplane = {0, 0, 0, 0}; /* mapped on winx winy */
|
||||
|
||||
/* final picture width and height (within disprect) */
|
||||
int rectx = 0, recty = 0;
|
||||
|
||||
/* Camera transform. Used by Freestyle, Eevee, and other draw manager engines.. */
|
||||
float winmat[4][4] = {{0}};
|
||||
|
||||
/* Clipping. */
|
||||
float clip_start = 0.0f;
|
||||
float clip_end = 0.0f;
|
||||
|
||||
/* main, scene, and its full copy of renderdata and world */
|
||||
struct Main *main = nullptr;
|
||||
Scene *scene = nullptr;
|
||||
RenderData r = {};
|
||||
char single_view_layer[MAX_NAME] = "";
|
||||
struct Object *camera_override = nullptr;
|
||||
|
||||
render::TilesHighlight tile_highlight;
|
||||
|
||||
/* NOTE: This is a minimal dependency graph and evaluated scene which is enough to access view
|
||||
* layer visibility and use for postprocessing (compositor and sequencer). */
|
||||
struct Depsgraph *pipeline_depsgraph = nullptr;
|
||||
Scene *pipeline_scene_eval = nullptr;
|
||||
|
||||
/* Compositor.
|
||||
* NOTE: Use bare pointer instead of smart pointer because the it is a fully opaque type. */
|
||||
render::Compositor *compositor = nullptr;
|
||||
Mutex compositor_mutex;
|
||||
|
||||
/* Callbacks for the corresponding base class method implementation. */
|
||||
bool (*prepare_viewlayer_cb)(void *handle,
|
||||
struct ViewLayer *vl,
|
||||
struct Depsgraph *depsgraph) = nullptr;
|
||||
void *prepare_vl_handle = nullptr;
|
||||
|
||||
RenderStats i = {};
|
||||
|
||||
/**
|
||||
* Optional report list which may be null (borrowed memory).
|
||||
* Callers to rendering functions are responsible for setting can clearing, see: #RE_SetReports.
|
||||
*/
|
||||
struct ReportList *reports = nullptr;
|
||||
|
||||
Vector<MovieWriter *> movie_writers;
|
||||
char viewname[MAX_NAME] = "";
|
||||
};
|
||||
|
||||
struct RenderDisplay {
|
||||
~RenderDisplay();
|
||||
|
||||
void free_gpu_context();
|
||||
|
||||
void ensure_system_gpu_context();
|
||||
void *ensure_blender_gpu_context();
|
||||
|
||||
void display_update(RenderResult *render_result, rcti *rect);
|
||||
void current_scene_update(struct Scene *scene);
|
||||
|
||||
void stats_draw(RenderStats *render_stats);
|
||||
void progress(float progress);
|
||||
|
||||
void draw_lock();
|
||||
void draw_unlock();
|
||||
|
||||
bool test_break();
|
||||
|
||||
/* Callbacks */
|
||||
void (*display_update_cb)(void *handle, RenderResult *rr, rcti *rect) = nullptr;
|
||||
void *duh = nullptr;
|
||||
void (*current_scene_update_cb)(void *handle, struct Scene *scene) = nullptr;
|
||||
void *suh = nullptr;
|
||||
|
||||
void (*stats_draw_cb)(void *handle, RenderStats *ri) = nullptr;
|
||||
void *sdh = nullptr;
|
||||
void (*progress_cb)(void *handle, float i) = nullptr;
|
||||
void *prh = nullptr;
|
||||
|
||||
void (*draw_lock_cb)(void *handle, bool lock) = nullptr;
|
||||
void *dlh = nullptr;
|
||||
bool (*test_break_cb)(void *handle) = nullptr;
|
||||
void *tbh = nullptr;
|
||||
|
||||
/* GPU contexts.
|
||||
* TODO: replace by a whole draw manager. */
|
||||
GHOST_IContext *system_gpu_context = nullptr;
|
||||
void *blender_gpu_context = nullptr;
|
||||
};
|
||||
|
||||
/* **************** defines ********************* */
|
||||
|
||||
/** #R.flag */
|
||||
#define R_ANIMATION 1 << 0
|
||||
/* Indicates that the render pipeline should not write its render result. This happens for instance
|
||||
* when the render pipeline uses the compositor, but the compositor node tree does not have a group
|
||||
* output node or a render layer input, and consequently no render result. In that case, the output
|
||||
* will be written from the File Output nodes, since the render pipeline will early fail if neither
|
||||
* a File Output nor a Group Output node exist in the scene. */
|
||||
#define R_SKIP_WRITE 1 << 1
|
||||
|
||||
} // namespace blender
|
||||
87
blender-5.2.0/source/blender/render/intern/texture_common.h
Normal file
87
blender-5.2.0/source/blender/render/intern/texture_common.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_color.h" /* IWYU pragma: keep. Used in macros. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define BRICONT \
|
||||
texres->tin = (texres->tin - 0.5f) * tex->contrast + tex->bright - 0.5f; \
|
||||
if (!(tex->flag & TEX_NO_CLAMP)) { \
|
||||
if (texres->tin < 0.0f) { \
|
||||
texres->tin = 0.0f; \
|
||||
} \
|
||||
else if (texres->tin > 1.0f) { \
|
||||
texres->tin = 1.0f; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
#define BRICONTRGB \
|
||||
texres->trgba[0] = tex->rfac * \
|
||||
((texres->trgba[0] - 0.5f) * tex->contrast + tex->bright - 0.5f); \
|
||||
texres->trgba[1] = tex->gfac * \
|
||||
((texres->trgba[1] - 0.5f) * tex->contrast + tex->bright - 0.5f); \
|
||||
texres->trgba[2] = tex->bfac * \
|
||||
((texres->trgba[2] - 0.5f) * tex->contrast + tex->bright - 0.5f); \
|
||||
if (!(tex->flag & TEX_NO_CLAMP)) { \
|
||||
if (texres->trgba[0] < 0.0f) { \
|
||||
texres->trgba[0] = 0.0f; \
|
||||
} \
|
||||
if (texres->trgba[1] < 0.0f) { \
|
||||
texres->trgba[1] = 0.0f; \
|
||||
} \
|
||||
if (texres->trgba[2] < 0.0f) { \
|
||||
texres->trgba[2] = 0.0f; \
|
||||
} \
|
||||
} \
|
||||
if (tex->saturation != 1.0f) { \
|
||||
float _hsv[3]; \
|
||||
rgb_to_hsv(texres->trgba[0], texres->trgba[1], texres->trgba[2], _hsv, _hsv + 1, _hsv + 2); \
|
||||
_hsv[1] *= tex->saturation; \
|
||||
hsv_to_rgb( \
|
||||
_hsv[0], _hsv[1], _hsv[2], &texres->trgba[0], &texres->trgba[1], &texres->trgba[2]); \
|
||||
if ((tex->saturation > 1.0f) && !(tex->flag & TEX_NO_CLAMP)) { \
|
||||
if (texres->trgba[0] < 0.0f) { \
|
||||
texres->trgba[0] = 0.0f; \
|
||||
} \
|
||||
if (texres->trgba[1] < 0.0f) { \
|
||||
texres->trgba[1] = 0.0f; \
|
||||
} \
|
||||
if (texres->trgba[2] < 0.0f) { \
|
||||
texres->trgba[2] = 0.0f; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
struct ImBuf;
|
||||
struct Image;
|
||||
struct ImagePool;
|
||||
struct Tex;
|
||||
struct TexResult;
|
||||
|
||||
/* `texture_image.cc` */
|
||||
|
||||
int imagewrap(struct Tex *tex,
|
||||
struct Image *ima,
|
||||
const float texvec[3],
|
||||
struct TexResult *texres,
|
||||
struct ImagePool *pool,
|
||||
bool skip_load_image);
|
||||
void image_sample(struct Image *ima,
|
||||
float fx,
|
||||
float fy,
|
||||
float dx,
|
||||
float dy,
|
||||
float result[4],
|
||||
struct ImagePool *pool);
|
||||
|
||||
} // namespace blender
|
||||
661
blender-5.2.0/source/blender/render/intern/texture_image.cc
Normal file
661
blender-5.2.0/source/blender/render/intern/texture_image.cc
Normal file
@@ -0,0 +1,661 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cfloat>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#ifndef WIN32
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_image.hh"
|
||||
|
||||
#include "RE_texture.h"
|
||||
|
||||
#include "texture_common.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void boxsample(ImBuf *ibuf,
|
||||
float minx,
|
||||
float miny,
|
||||
float maxx,
|
||||
float maxy,
|
||||
TexResult *texres,
|
||||
const short imaprepeat,
|
||||
const short imapextend);
|
||||
|
||||
/* *********** IMAGEWRAPPING ****************** */
|
||||
|
||||
/* x and y have to be checked for image size */
|
||||
static void ibuf_get_color(float col[4], const ImBuf *ibuf, int x, int y)
|
||||
{
|
||||
const int64_t ofs = int64_t(y) * ibuf->x + x;
|
||||
|
||||
if (ibuf->float_data()) {
|
||||
if (ibuf->channels == 4) {
|
||||
const float *fp = ibuf->float_data() + 4 * ofs;
|
||||
copy_v4_v4(col, fp);
|
||||
}
|
||||
else if (ibuf->channels == 3) {
|
||||
const float *fp = ibuf->float_data() + 3 * ofs;
|
||||
copy_v3_v3(col, fp);
|
||||
col[3] = 1.0f;
|
||||
}
|
||||
else {
|
||||
const float *fp = ibuf->float_data() + ofs;
|
||||
col[0] = col[1] = col[2] = col[3] = *fp;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const uchar *rect = ibuf->byte_data() + 4 * ofs;
|
||||
|
||||
col[0] = float(rect[0]) * (1.0f / 255.0f);
|
||||
col[1] = float(rect[1]) * (1.0f / 255.0f);
|
||||
col[2] = float(rect[2]) * (1.0f / 255.0f);
|
||||
col[3] = float(rect[3]) * (1.0f / 255.0f);
|
||||
|
||||
/* Bytes are internally straight, however render pipeline seems to expect pre-multiplied. */
|
||||
col[0] *= col[3];
|
||||
col[1] *= col[3];
|
||||
col[2] *= col[3];
|
||||
}
|
||||
}
|
||||
|
||||
int imagewrap(Tex *tex,
|
||||
Image *ima,
|
||||
const float texvec[3],
|
||||
TexResult *texres,
|
||||
ImagePool *pool,
|
||||
const bool skip_load_image)
|
||||
{
|
||||
float fx, fy;
|
||||
int x, y, retval;
|
||||
int xi, yi; /* original values */
|
||||
|
||||
texres->tin = texres->trgba[3] = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f;
|
||||
|
||||
retval = TEX_RGB;
|
||||
|
||||
/* quick tests */
|
||||
if (ima == nullptr) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* hack for icon render */
|
||||
if (skip_load_image && !BKE_image_has_loaded_ibuf(ima)) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
ImageUser *iuser = &tex->iuser;
|
||||
ImageUser local_iuser;
|
||||
if (ima->source == IMA_SRC_TILED) {
|
||||
/* tex->iuser might be shared by threads, so create a local copy. */
|
||||
local_iuser = tex->iuser;
|
||||
iuser = &local_iuser;
|
||||
|
||||
float new_uv[2];
|
||||
iuser->tile = BKE_image_get_tile_from_pos(ima, texvec, new_uv, nullptr);
|
||||
fx = new_uv[0];
|
||||
fy = new_uv[1];
|
||||
}
|
||||
else {
|
||||
fx = texvec[0];
|
||||
fy = texvec[1];
|
||||
}
|
||||
|
||||
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(ima, iuser, pool);
|
||||
|
||||
ima->flag |= IMA_USED_FOR_RENDER;
|
||||
|
||||
if (ibuf == nullptr || (ibuf->byte_data() == nullptr && ibuf->float_data() == nullptr)) {
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* setup mapping */
|
||||
if (tex->imaflag & TEX_IMAROT) {
|
||||
std::swap(fx, fy);
|
||||
}
|
||||
|
||||
if (tex->extend == TEX_CHECKER) {
|
||||
int xs, ys;
|
||||
|
||||
xs = int(floor(fx));
|
||||
ys = int(floor(fy));
|
||||
fx -= xs;
|
||||
fy -= ys;
|
||||
|
||||
if ((tex->flag & TEX_CHECKER_ODD) == 0) {
|
||||
if ((xs + ys) & 1) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
if (ima) {
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
if ((tex->flag & TEX_CHECKER_EVEN) == 0) {
|
||||
if ((xs + ys) & 1) {
|
||||
if (ima) {
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
/* scale around center, (0.5, 0.5) */
|
||||
if (tex->checkerdist < 1.0f) {
|
||||
fx = (fx - 0.5f) / (1.0f - tex->checkerdist) + 0.5f;
|
||||
fy = (fy - 0.5f) / (1.0f - tex->checkerdist) + 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
x = xi = int(floorf(fx * ibuf->x));
|
||||
y = yi = int(floorf(fy * ibuf->y));
|
||||
|
||||
if (tex->extend == TEX_CLIPCUBE) {
|
||||
if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y || texvec[2] < -1.0f || texvec[2] > 1.0f) {
|
||||
if (ima) {
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
else if (ELEM(tex->extend, TEX_CLIP, TEX_CHECKER)) {
|
||||
if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y) {
|
||||
if (ima) {
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tex->extend == TEX_EXTEND) {
|
||||
if (x >= ibuf->x) {
|
||||
x = ibuf->x - 1;
|
||||
}
|
||||
else if (x < 0) {
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
x = x % ibuf->x;
|
||||
if (x < 0) {
|
||||
x += ibuf->x;
|
||||
}
|
||||
}
|
||||
if (tex->extend == TEX_EXTEND) {
|
||||
if (y >= ibuf->y) {
|
||||
y = ibuf->y - 1;
|
||||
}
|
||||
else if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
y = y % ibuf->y;
|
||||
if (y < 0) {
|
||||
y += ibuf->y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep this before interpolation #29761. */
|
||||
if (ima) {
|
||||
if ((tex->imaflag & TEX_USEALPHA) && (ima->alpha_mode != IMA_ALPHA_IGNORE)) {
|
||||
if ((tex->imaflag & TEX_CALCALPHA) == 0) {
|
||||
texres->talpha = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* interpolate */
|
||||
if (tex->imaflag & TEX_INTERPOL) {
|
||||
float filterx, filtery;
|
||||
filterx = (0.5f * tex->filtersize) / ibuf->x;
|
||||
filtery = (0.5f * tex->filtersize) / ibuf->y;
|
||||
|
||||
/* Important that this value is wrapped #27782.
|
||||
* this applies the modifications made by the checks above,
|
||||
* back to the floating point values */
|
||||
fx -= float(xi - x) / float(ibuf->x);
|
||||
fy -= float(yi - y) / float(ibuf->y);
|
||||
|
||||
boxsample(ibuf,
|
||||
fx - filterx,
|
||||
fy - filtery,
|
||||
fx + filterx,
|
||||
fy + filtery,
|
||||
texres,
|
||||
(tex->extend == TEX_REPEAT),
|
||||
(tex->extend == TEX_EXTEND));
|
||||
}
|
||||
else { /* no filtering */
|
||||
ibuf_get_color(texres->trgba, ibuf, x, y);
|
||||
}
|
||||
|
||||
if (texres->talpha) {
|
||||
texres->tin = texres->trgba[3];
|
||||
}
|
||||
else if (tex->imaflag & TEX_CALCALPHA) {
|
||||
texres->trgba[3] = texres->tin = std::max(
|
||||
{texres->trgba[0], texres->trgba[1], texres->trgba[2]});
|
||||
}
|
||||
else {
|
||||
texres->trgba[3] = texres->tin = 1.0;
|
||||
}
|
||||
|
||||
if (tex->flag & TEX_NEGALPHA) {
|
||||
texres->trgba[3] = 1.0f - texres->trgba[3];
|
||||
}
|
||||
|
||||
/* De-pre-multiply, this is being pre-multiplied in #shade_input_do_shade()
|
||||
* do not de-pre-multiply for generated alpha, it is already in straight. */
|
||||
if (texres->trgba[3] != 1.0f && texres->trgba[3] > 1e-4f && !(tex->imaflag & TEX_CALCALPHA)) {
|
||||
fx = 1.0f / texres->trgba[3];
|
||||
texres->trgba[0] *= fx;
|
||||
texres->trgba[1] *= fx;
|
||||
texres->trgba[2] *= fx;
|
||||
}
|
||||
|
||||
if (ima) {
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
}
|
||||
|
||||
BRICONTRGB;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void clipx_rctf_swap(rctf *stack, short *count, float x1, float x2)
|
||||
{
|
||||
rctf *rf, *newrct;
|
||||
short a;
|
||||
|
||||
a = *count;
|
||||
rf = stack;
|
||||
for (; a > 0; a--) {
|
||||
if (rf->xmin < x1) {
|
||||
if (rf->xmax < x1) {
|
||||
rf->xmin += (x2 - x1);
|
||||
rf->xmax += (x2 - x1);
|
||||
}
|
||||
else {
|
||||
rf->xmax = std::min(rf->xmax, x2);
|
||||
newrct = stack + *count;
|
||||
(*count)++;
|
||||
|
||||
newrct->xmax = x2;
|
||||
newrct->xmin = rf->xmin + (x2 - x1);
|
||||
newrct->ymin = rf->ymin;
|
||||
newrct->ymax = rf->ymax;
|
||||
|
||||
if (newrct->xmin == newrct->xmax) {
|
||||
(*count)--;
|
||||
}
|
||||
|
||||
rf->xmin = x1;
|
||||
}
|
||||
}
|
||||
else if (rf->xmax > x2) {
|
||||
if (rf->xmin > x2) {
|
||||
rf->xmin -= (x2 - x1);
|
||||
rf->xmax -= (x2 - x1);
|
||||
}
|
||||
else {
|
||||
rf->xmin = std::max(rf->xmin, x1);
|
||||
newrct = stack + *count;
|
||||
(*count)++;
|
||||
|
||||
newrct->xmin = x1;
|
||||
newrct->xmax = rf->xmax - (x2 - x1);
|
||||
newrct->ymin = rf->ymin;
|
||||
newrct->ymax = rf->ymax;
|
||||
|
||||
if (newrct->xmin == newrct->xmax) {
|
||||
(*count)--;
|
||||
}
|
||||
|
||||
rf->xmax = x2;
|
||||
}
|
||||
}
|
||||
rf++;
|
||||
}
|
||||
}
|
||||
|
||||
static void clipy_rctf_swap(rctf *stack, short *count, float y1, float y2)
|
||||
{
|
||||
rctf *rf, *newrct;
|
||||
short a;
|
||||
|
||||
a = *count;
|
||||
rf = stack;
|
||||
for (; a > 0; a--) {
|
||||
if (rf->ymin < y1) {
|
||||
if (rf->ymax < y1) {
|
||||
rf->ymin += (y2 - y1);
|
||||
rf->ymax += (y2 - y1);
|
||||
}
|
||||
else {
|
||||
rf->ymax = std::min(rf->ymax, y2);
|
||||
newrct = stack + *count;
|
||||
(*count)++;
|
||||
|
||||
newrct->ymax = y2;
|
||||
newrct->ymin = rf->ymin + (y2 - y1);
|
||||
newrct->xmin = rf->xmin;
|
||||
newrct->xmax = rf->xmax;
|
||||
|
||||
if (newrct->ymin == newrct->ymax) {
|
||||
(*count)--;
|
||||
}
|
||||
|
||||
rf->ymin = y1;
|
||||
}
|
||||
}
|
||||
else if (rf->ymax > y2) {
|
||||
if (rf->ymin > y2) {
|
||||
rf->ymin -= (y2 - y1);
|
||||
rf->ymax -= (y2 - y1);
|
||||
}
|
||||
else {
|
||||
rf->ymin = std::max(rf->ymin, y1);
|
||||
newrct = stack + *count;
|
||||
(*count)++;
|
||||
|
||||
newrct->ymin = y1;
|
||||
newrct->ymax = rf->ymax - (y2 - y1);
|
||||
newrct->xmin = rf->xmin;
|
||||
newrct->xmax = rf->xmax;
|
||||
|
||||
if (newrct->ymin == newrct->ymax) {
|
||||
(*count)--;
|
||||
}
|
||||
|
||||
rf->ymax = y2;
|
||||
}
|
||||
}
|
||||
rf++;
|
||||
}
|
||||
}
|
||||
|
||||
static float square_rctf(const rctf *rf)
|
||||
{
|
||||
float x, y;
|
||||
|
||||
x = BLI_rctf_size_x(rf);
|
||||
y = BLI_rctf_size_y(rf);
|
||||
return x * y;
|
||||
}
|
||||
|
||||
static float clipx_rctf(rctf *rf, float x1, float x2)
|
||||
{
|
||||
float size;
|
||||
|
||||
size = BLI_rctf_size_x(rf);
|
||||
|
||||
rf->xmin = std::max(rf->xmin, x1);
|
||||
rf->xmax = std::min(rf->xmax, x2);
|
||||
if (rf->xmin > rf->xmax) {
|
||||
rf->xmin = rf->xmax;
|
||||
return 0.0;
|
||||
}
|
||||
if (size != 0.0f) {
|
||||
return BLI_rctf_size_x(rf) / size;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
static float clipy_rctf(rctf *rf, float y1, float y2)
|
||||
{
|
||||
float size;
|
||||
|
||||
size = BLI_rctf_size_y(rf);
|
||||
|
||||
rf->ymin = std::max(rf->ymin, y1);
|
||||
rf->ymax = std::min(rf->ymax, y2);
|
||||
|
||||
if (rf->ymin > rf->ymax) {
|
||||
rf->ymin = rf->ymax;
|
||||
return 0.0;
|
||||
}
|
||||
if (size != 0.0f) {
|
||||
return BLI_rctf_size_y(rf) / size;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
static void boxsampleclip(ImBuf *ibuf, const rctf *rf, TexResult *texres)
|
||||
{
|
||||
/* Sample box, is clipped already, and minx etc. have been set at ibuf size.
|
||||
* Enlarge with anti-aliased edges of the pixels. */
|
||||
|
||||
float muly, mulx, div, col[4];
|
||||
int x, y, startx, endx, starty, endy;
|
||||
|
||||
startx = int(floor(rf->xmin));
|
||||
endx = int(floor(rf->xmax));
|
||||
starty = int(floor(rf->ymin));
|
||||
endy = int(floor(rf->ymax));
|
||||
|
||||
startx = std::max(startx, 0);
|
||||
starty = std::max(starty, 0);
|
||||
if (endx >= ibuf->x) {
|
||||
endx = ibuf->x - 1;
|
||||
}
|
||||
if (endy >= ibuf->y) {
|
||||
endy = ibuf->y - 1;
|
||||
}
|
||||
|
||||
if (starty == endy && startx == endx) {
|
||||
ibuf_get_color(texres->trgba, ibuf, startx, starty);
|
||||
}
|
||||
else {
|
||||
div = texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = texres->trgba[3] = 0.0;
|
||||
for (y = starty; y <= endy; y++) {
|
||||
|
||||
muly = 1.0;
|
||||
|
||||
if (starty == endy) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
if (y == starty) {
|
||||
muly = 1.0f - (rf->ymin - y);
|
||||
}
|
||||
if (y == endy) {
|
||||
muly = (rf->ymax - y);
|
||||
}
|
||||
}
|
||||
|
||||
if (startx == endx) {
|
||||
mulx = muly;
|
||||
|
||||
ibuf_get_color(col, ibuf, startx, y);
|
||||
madd_v4_v4fl(texres->trgba, col, mulx);
|
||||
div += mulx;
|
||||
}
|
||||
else {
|
||||
for (x = startx; x <= endx; x++) {
|
||||
mulx = muly;
|
||||
if (x == startx) {
|
||||
mulx *= 1.0f - (rf->xmin - x);
|
||||
}
|
||||
if (x == endx) {
|
||||
mulx *= (rf->xmax - x);
|
||||
}
|
||||
|
||||
ibuf_get_color(col, ibuf, x, y);
|
||||
/* TODO(jbakker): No need to do manual optimization. Branching is slower than multiplying
|
||||
* with 1. */
|
||||
if (mulx == 1.0f) {
|
||||
add_v4_v4(texres->trgba, col);
|
||||
div += 1.0f;
|
||||
}
|
||||
else {
|
||||
madd_v4_v4fl(texres->trgba, col, mulx);
|
||||
div += mulx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (div != 0.0f) {
|
||||
div = 1.0f / div;
|
||||
mul_v4_fl(texres->trgba, div);
|
||||
}
|
||||
else {
|
||||
zero_v4(texres->trgba);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void boxsample(ImBuf *ibuf,
|
||||
float minx,
|
||||
float miny,
|
||||
float maxx,
|
||||
float maxy,
|
||||
TexResult *texres,
|
||||
const short imaprepeat,
|
||||
const short imapextend)
|
||||
{
|
||||
/* Sample box, performs clip. minx etc are in range 0.0 - 1.0 .
|
||||
* Enlarge with anti-aliased edges of pixels.
|
||||
* If variable 'imaprepeat' has been set, the
|
||||
* clipped-away parts are sampled as well.
|
||||
*/
|
||||
/* NOTE: actually minx etc isn't in the proper range...
|
||||
* this due to filter size and offset vectors for bump. */
|
||||
/* NOTE: talpha must be initialized. */
|
||||
/* NOTE: even when 'imaprepeat' is set, this can only repeat once in any direction.
|
||||
* the point which min/max is derived from is assumed to be wrapped. */
|
||||
TexResult texr;
|
||||
rctf *rf, stack[8];
|
||||
float opp, tot, alphaclip = 1.0;
|
||||
short count = 1;
|
||||
|
||||
rf = stack;
|
||||
rf->xmin = minx * (ibuf->x);
|
||||
rf->xmax = maxx * (ibuf->x);
|
||||
rf->ymin = miny * (ibuf->y);
|
||||
rf->ymax = maxy * (ibuf->y);
|
||||
|
||||
texr.talpha = texres->talpha; /* is read by boxsample_clip */
|
||||
|
||||
if (imapextend) {
|
||||
CLAMP(rf->xmin, 0.0f, ibuf->x - 1);
|
||||
CLAMP(rf->xmax, 0.0f, ibuf->x - 1);
|
||||
}
|
||||
else if (imaprepeat) {
|
||||
clipx_rctf_swap(stack, &count, 0.0, float(ibuf->x));
|
||||
}
|
||||
else {
|
||||
alphaclip = clipx_rctf(rf, 0.0, float(ibuf->x));
|
||||
|
||||
if (alphaclip <= 0.0f) {
|
||||
texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (imapextend) {
|
||||
CLAMP(rf->ymin, 0.0f, ibuf->y - 1);
|
||||
CLAMP(rf->ymax, 0.0f, ibuf->y - 1);
|
||||
}
|
||||
else if (imaprepeat) {
|
||||
clipy_rctf_swap(stack, &count, 0.0, float(ibuf->y));
|
||||
}
|
||||
else {
|
||||
alphaclip *= clipy_rctf(rf, 0.0, float(ibuf->y));
|
||||
|
||||
if (alphaclip <= 0.0f) {
|
||||
texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
tot = texres->trgba[0] = texres->trgba[2] = texres->trgba[1] = texres->trgba[3] = 0.0;
|
||||
while (count--) {
|
||||
boxsampleclip(ibuf, rf, &texr);
|
||||
|
||||
opp = square_rctf(rf);
|
||||
tot += opp;
|
||||
|
||||
texres->trgba[0] += opp * texr.trgba[0];
|
||||
texres->trgba[1] += opp * texr.trgba[1];
|
||||
texres->trgba[2] += opp * texr.trgba[2];
|
||||
if (texres->talpha) {
|
||||
texres->trgba[3] += opp * texr.trgba[3];
|
||||
}
|
||||
rf++;
|
||||
}
|
||||
if (tot != 0.0f) {
|
||||
texres->trgba[0] /= tot;
|
||||
texres->trgba[1] /= tot;
|
||||
texres->trgba[2] /= tot;
|
||||
if (texres->talpha) {
|
||||
texres->trgba[3] /= tot;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
boxsampleclip(ibuf, rf, texres);
|
||||
}
|
||||
|
||||
if (texres->talpha == 0) {
|
||||
texres->trgba[3] = 1.0;
|
||||
}
|
||||
|
||||
if (alphaclip != 1.0f) {
|
||||
/* Pre-multiply it all. */
|
||||
texres->trgba[0] *= alphaclip;
|
||||
texres->trgba[1] *= alphaclip;
|
||||
texres->trgba[2] *= alphaclip;
|
||||
texres->trgba[3] *= alphaclip;
|
||||
}
|
||||
}
|
||||
|
||||
void image_sample(
|
||||
Image *ima, float fx, float fy, float dx, float dy, float result[4], ImagePool *pool)
|
||||
{
|
||||
TexResult texres;
|
||||
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(ima, nullptr, pool);
|
||||
|
||||
if (UNLIKELY(ibuf == nullptr)) {
|
||||
zero_v4(result);
|
||||
return;
|
||||
}
|
||||
|
||||
texres.talpha = true; /* boxsample expects to be initialized */
|
||||
boxsample(ibuf, fx, fy, fx + dx, fy + dy, &texres, 0, 1);
|
||||
copy_v4_v4(result, texres.trgba);
|
||||
|
||||
ima->flag |= IMA_USED_FOR_RENDER;
|
||||
|
||||
BKE_image_pool_release_ibuf(ima, ibuf, pool);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
595
blender-5.2.0/source/blender/render/intern/texture_margin.cc
Normal file
595
blender-5.2.0/source/blender/render/intern/texture_margin.cc
Normal file
@@ -0,0 +1,595 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_math_geom.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_mesh_mapping.hh"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_interp.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "zbuf.h" /* For rasterizer (#ZSpan and associated functions). */
|
||||
|
||||
#include "RE_texture_margin.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace render::texturemargin {
|
||||
|
||||
/**
|
||||
* The map class contains both a pixel map which maps out face indices for all UV-polygons and
|
||||
* adjacency tables.
|
||||
*/
|
||||
class TextureMarginMap {
|
||||
static const int directions[8][2];
|
||||
static const int distances[8];
|
||||
|
||||
/** Maps UV-edges to their corresponding UV-edge. */
|
||||
Vector<int> loop_adjacency_map_;
|
||||
/** Maps UV-edges to their corresponding face. */
|
||||
Array<int> loop_to_face_map_;
|
||||
|
||||
int w_, h_;
|
||||
float uv_offset_[2];
|
||||
Vector<uint32_t> pixel_data_;
|
||||
ZSpan zspan_;
|
||||
uint32_t value_to_store_;
|
||||
bool write_mask_;
|
||||
char *mask_;
|
||||
|
||||
OffsetIndices<int> faces_;
|
||||
Span<int> corner_edges_;
|
||||
Span<float2> uv_map_;
|
||||
int totedge_;
|
||||
|
||||
public:
|
||||
TextureMarginMap(size_t w,
|
||||
size_t h,
|
||||
const float uv_offset[2],
|
||||
const int totedge,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_edges,
|
||||
const Span<float2> uv_map)
|
||||
: w_(w),
|
||||
h_(h),
|
||||
faces_(faces),
|
||||
corner_edges_(corner_edges),
|
||||
uv_map_(uv_map),
|
||||
totedge_(totedge)
|
||||
{
|
||||
copy_v2_v2(uv_offset_, uv_offset);
|
||||
|
||||
pixel_data_.resize(w_ * h_, 0xFFFFFFFF);
|
||||
|
||||
zbuf_alloc_span(&zspan_, w_, h_);
|
||||
|
||||
build_tables();
|
||||
}
|
||||
|
||||
~TextureMarginMap()
|
||||
{
|
||||
zbuf_free_span(&zspan_);
|
||||
}
|
||||
|
||||
void set_pixel(int x, int y, uint32_t value)
|
||||
{
|
||||
BLI_assert(x < w_);
|
||||
BLI_assert(x >= 0);
|
||||
pixel_data_[y * w_ + x] = value;
|
||||
}
|
||||
|
||||
uint32_t get_pixel(int x, int y) const
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= w_ || y >= h_) {
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
return pixel_data_[y * w_ + x];
|
||||
}
|
||||
|
||||
void rasterize_tri(float *v1, float *v2, float *v3, uint32_t value, char *mask, bool writemask)
|
||||
{
|
||||
/* NOTE: This is not thread safe, because the value to be written by the rasterizer is
|
||||
* a class member. If this is ever made multi-threaded each thread needs to get its own. */
|
||||
value_to_store_ = value;
|
||||
mask_ = mask;
|
||||
write_mask_ = writemask;
|
||||
zspan_scanconvert(
|
||||
&zspan_, this, &(v1[0]), &(v2[0]), &(v3[0]), TextureMarginMap::zscan_store_pixel);
|
||||
}
|
||||
|
||||
static void zscan_store_pixel(
|
||||
void *map, int x, int y, [[maybe_unused]] float u, [[maybe_unused]] float v)
|
||||
{
|
||||
/* NOTE: Not thread safe, see comment above. */
|
||||
TextureMarginMap *m = static_cast<TextureMarginMap *>(map);
|
||||
if (m->mask_) {
|
||||
if (m->write_mask_) {
|
||||
/* if there is a mask and write_mask_ is true, write to the mask */
|
||||
m->mask_[y * m->w_ + x] = 1;
|
||||
m->set_pixel(x, y, m->value_to_store_);
|
||||
}
|
||||
else {
|
||||
/* if there is a mask and write_mask_ is false, read the mask
|
||||
* to decide if the map needs to be written
|
||||
*/
|
||||
if (m->mask_[y * m->w_ + x] != 0) {
|
||||
m->set_pixel(x, y, m->value_to_store_);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m->set_pixel(x, y, m->value_to_store_);
|
||||
}
|
||||
}
|
||||
|
||||
/* The map contains 2 kinds of pixels: DijkstraPixels and face indices. The top bit determines
|
||||
* what kind it is. With the top bit set, it is a 'dijkstra' pixel. The bottom 4 bits encode the
|
||||
* direction of the shortest path and the remaining 27 bits are used to store the distance. If
|
||||
* the top bit is not set, the rest of the bits is used to store the face index.
|
||||
*/
|
||||
#define PackDijkstraPixel(dist, dir) (0x80000000 + ((dist) << 4) + (dir))
|
||||
#define DijkstraPixelGetDistance(dp) (((dp) ^ 0x80000000) >> 4)
|
||||
#define DijkstraPixelGetDirection(dp) ((dp) & 0xF)
|
||||
#define IsDijkstraPixel(dp) ((dp) & 0x80000000)
|
||||
#define DijkstraPixelIsUnset(dp) ((dp) == 0xFFFFFFFF)
|
||||
|
||||
/**
|
||||
* Use dijkstra's algorithm to 'grow' a border around the polygons marked in the map.
|
||||
* For each pixel mark which direction is the shortest way to a face.
|
||||
*/
|
||||
void grow_dijkstra(int margin)
|
||||
{
|
||||
class DijkstraActivePixel {
|
||||
public:
|
||||
DijkstraActivePixel(int dist, int _x, int _y) : distance(dist), x(_x), y(_y) {}
|
||||
int distance;
|
||||
int x, y;
|
||||
};
|
||||
auto cmp_dijkstrapixel_fun = [](DijkstraActivePixel const &a1, DijkstraActivePixel const &a2) {
|
||||
return a1.distance > a2.distance;
|
||||
};
|
||||
|
||||
Vector<DijkstraActivePixel> active_pixels;
|
||||
for (int y = 0; y < h_; y++) {
|
||||
for (int x = 0; x < w_; x++) {
|
||||
if (DijkstraPixelIsUnset(get_pixel(x, y))) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int xx = x - directions[i][0];
|
||||
int yy = y - directions[i][1];
|
||||
|
||||
if (xx >= 0 && xx < w_ && yy >= 0 && yy < w_ && !IsDijkstraPixel(get_pixel(xx, yy))) {
|
||||
set_pixel(x, y, PackDijkstraPixel(distances[i], i));
|
||||
active_pixels.append(DijkstraActivePixel(distances[i], x, y));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Not strictly needed because at this point it already is a heap. */
|
||||
#if 0
|
||||
std::make_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
|
||||
#endif
|
||||
|
||||
while (active_pixels.size()) {
|
||||
std::pop_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
|
||||
DijkstraActivePixel p = active_pixels.pop_last();
|
||||
|
||||
int dist = p.distance;
|
||||
|
||||
if (dist < 2 * (margin + 1)) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = p.x + directions[i][0];
|
||||
int y = p.y + directions[i][1];
|
||||
if (x >= 0 && x < w_ && y >= 0 && y < h_) {
|
||||
uint32_t dp = get_pixel(x, y);
|
||||
if (IsDijkstraPixel(dp) && (DijkstraPixelGetDistance(dp) > dist + distances[i])) {
|
||||
BLI_assert(DijkstraPixelGetDirection(dp) != i);
|
||||
set_pixel(x, y, PackDijkstraPixel(dist + distances[i], i));
|
||||
active_pixels.append(DijkstraActivePixel(dist + distances[i], x, y));
|
||||
std::push_heap(active_pixels.begin(), active_pixels.end(), cmp_dijkstrapixel_fun);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk over the map and for margin pixels follow the direction stored in the bottom 3
|
||||
* bits back to the face.
|
||||
* Then look up the pixel from the next face.
|
||||
*/
|
||||
void lookup_pixels(ImBuf *ibuf, char *mask, int maxPolygonSteps)
|
||||
{
|
||||
float4 *ibuf_ptr_fl = reinterpret_cast<float4 *>(ibuf->float_data_for_write());
|
||||
uchar4 *ibuf_ptr_ch = reinterpret_cast<uchar4 *>(ibuf->byte_data_for_write());
|
||||
size_t pixel_index = 0;
|
||||
for (int y = 0; y < h_; y++) {
|
||||
for (int x = 0; x < w_; x++) {
|
||||
uint32_t dp = pixel_data_[pixel_index];
|
||||
if (IsDijkstraPixel(dp) && !DijkstraPixelIsUnset(dp)) {
|
||||
int dist = DijkstraPixelGetDistance(dp);
|
||||
int direction = DijkstraPixelGetDirection(dp);
|
||||
|
||||
int xx = x;
|
||||
int yy = y;
|
||||
|
||||
/* Follow the dijkstra directions to find the face this margin pixels belongs to. */
|
||||
while (dist > 0) {
|
||||
xx -= directions[direction][0];
|
||||
yy -= directions[direction][1];
|
||||
dp = get_pixel(xx, yy);
|
||||
dist -= distances[direction];
|
||||
BLI_assert(!dist || (dist == DijkstraPixelGetDistance(dp)));
|
||||
direction = DijkstraPixelGetDirection(dp);
|
||||
}
|
||||
|
||||
uint32_t face = get_pixel(xx, yy);
|
||||
|
||||
BLI_assert(!IsDijkstraPixel(face));
|
||||
|
||||
float destX, destY;
|
||||
|
||||
int other_poly;
|
||||
bool found_pixel_in_polygon = false;
|
||||
if (lookup_pixel_polygon_neighborhood(x, y, &face, &destX, &destY, &other_poly)) {
|
||||
|
||||
for (int i = 0; i < maxPolygonSteps; i++) {
|
||||
/* Force to pixel grid. */
|
||||
int nx = int(round(destX));
|
||||
int ny = int(round(destY));
|
||||
uint32_t polygon_from_map = get_pixel(nx, ny);
|
||||
if (other_poly == polygon_from_map) {
|
||||
found_pixel_in_polygon = true;
|
||||
break;
|
||||
}
|
||||
|
||||
float dist_to_edge;
|
||||
/* Look up again, but starting from the face we were expected to land in. */
|
||||
if (!lookup_pixel(nx, ny, other_poly, &destX, &destY, &other_poly, &dist_to_edge)) {
|
||||
found_pixel_in_polygon = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_pixel_in_polygon) {
|
||||
if (ibuf_ptr_fl) {
|
||||
ibuf_ptr_fl[pixel_index] = imbuf::interpolate_bilinear_border_fl(
|
||||
ibuf, destX, destY);
|
||||
}
|
||||
if (ibuf_ptr_ch) {
|
||||
ibuf_ptr_ch[pixel_index] = imbuf::interpolate_bilinear_border_byte(
|
||||
ibuf, destX, destY);
|
||||
}
|
||||
/* Add our new pixels to the assigned pixel map. */
|
||||
mask[pixel_index] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (DijkstraPixelIsUnset(dp) || !IsDijkstraPixel(dp)) {
|
||||
/* These are not margin pixels, make sure the extend filter which is run after this step
|
||||
* leaves them alone.
|
||||
*/
|
||||
mask[pixel_index] = 1;
|
||||
}
|
||||
pixel_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
float2 uv_to_xy(const float2 &uv_map) const
|
||||
{
|
||||
float2 ret;
|
||||
ret.x = (((uv_map[0] - uv_offset_[0]) * w_) - (0.5f + 0.001f));
|
||||
ret.y = (((uv_map[1] - uv_offset_[1]) * h_) - (0.5f + 0.001f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void build_tables()
|
||||
{
|
||||
loop_to_face_map_ = bke::mesh::build_corner_to_face_map(faces_);
|
||||
|
||||
loop_adjacency_map_.resize(corner_edges_.size(), -1);
|
||||
|
||||
Vector<int> tmpmap;
|
||||
tmpmap.resize(totedge_, -1);
|
||||
|
||||
for (const int64_t i : corner_edges_.index_range()) {
|
||||
int edge = corner_edges_[i];
|
||||
if (tmpmap[edge] == -1) {
|
||||
loop_adjacency_map_[i] = -1;
|
||||
tmpmap[edge] = i;
|
||||
}
|
||||
else {
|
||||
BLI_assert(tmpmap[edge] >= 0);
|
||||
loop_adjacency_map_[i] = tmpmap[edge];
|
||||
loop_adjacency_map_[tmpmap[edge]] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call lookup_pixel for the start_poly. If that fails, try the adjacent polygons as well.
|
||||
* Because the Dijkstra is not very exact in determining which face is the closest, the
|
||||
* face we need can be the one next to the one the Dijkstra map provides. To prevent missing
|
||||
* pixels also check the neighboring polygons.
|
||||
*/
|
||||
bool lookup_pixel_polygon_neighborhood(
|
||||
float x, float y, uint32_t *r_start_poly, float *r_destx, float *r_desty, int *r_other_poly)
|
||||
{
|
||||
float found_dist;
|
||||
if (lookup_pixel(x, y, *r_start_poly, r_destx, r_desty, r_other_poly, &found_dist)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int loopstart = faces_[*r_start_poly].start();
|
||||
int totloop = faces_[*r_start_poly].size();
|
||||
|
||||
float destx, desty;
|
||||
int foundpoly;
|
||||
|
||||
float mindist = -1.0f;
|
||||
|
||||
/* Loop over all adjacent polygons and determine which edge is closest.
|
||||
* This could be optimized by only inspecting neighbors which are on the edge of an island.
|
||||
* But it seems fast enough for now and that would add a lot of complexity. */
|
||||
for (int i = 0; i < totloop; i++) {
|
||||
int otherloop = loop_adjacency_map_[i + loopstart];
|
||||
|
||||
if (otherloop < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t face = loop_to_face_map_[otherloop];
|
||||
|
||||
if (lookup_pixel(x, y, face, &destx, &desty, &foundpoly, &found_dist)) {
|
||||
if (mindist < 0.0f || found_dist < mindist) {
|
||||
mindist = found_dist;
|
||||
*r_other_poly = foundpoly;
|
||||
*r_destx = destx;
|
||||
*r_desty = desty;
|
||||
*r_start_poly = face;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mindist >= 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find which edge of the src_poly is closest to x,y. Look up its adjacent UV-edge and face.
|
||||
* Then return the location of the equivalent pixel in the other face.
|
||||
* Returns true if a new pixel location was found, false if it wasn't, which can happen if the
|
||||
* margin pixel is on a corner, or the UV-edge doesn't have an adjacent face.
|
||||
*/
|
||||
bool lookup_pixel(float x,
|
||||
float y,
|
||||
int src_poly,
|
||||
float *r_destx,
|
||||
float *r_desty,
|
||||
int *r_other_poly,
|
||||
float *r_dist_to_edge)
|
||||
{
|
||||
float2 point(x, y);
|
||||
|
||||
*r_destx = *r_desty = 0;
|
||||
|
||||
int found_edge = -1;
|
||||
float found_dist = -1;
|
||||
float found_t = 0;
|
||||
|
||||
/* Find the closest edge on which the point x,y can be projected.
|
||||
*/
|
||||
for (size_t i = 0; i < faces_[src_poly].size(); i++) {
|
||||
int l1 = faces_[src_poly].start() + i;
|
||||
int l2 = l1 + 1;
|
||||
if (l2 >= faces_[src_poly].start() + faces_[src_poly].size()) {
|
||||
l2 = faces_[src_poly].start();
|
||||
}
|
||||
/* edge points */
|
||||
float2 edgepoint1 = uv_to_xy(uv_map_[l1]);
|
||||
float2 edgepoint2 = uv_to_xy(uv_map_[l2]);
|
||||
/* Vector AB is the vector from the first edge point to the second edge point.
|
||||
* Vector AP is the vector from the first edge point to our point under investigation. */
|
||||
float2 ab = edgepoint2 - edgepoint1;
|
||||
float2 ap = point - edgepoint1;
|
||||
|
||||
/* Project ap onto ab. */
|
||||
float dotv = math::dot(ab, ap);
|
||||
|
||||
float ablensq = math::length_squared(ab);
|
||||
|
||||
float t = dotv / ablensq;
|
||||
|
||||
if (t >= 0.0 && t <= 1.0) {
|
||||
|
||||
/* Find the point on the edge closest to P */
|
||||
float2 reflect_point = edgepoint1 + (t * ab);
|
||||
/* This is the vector to P, so 90 degrees out from the edge. */
|
||||
float2 reflect_vec = reflect_point - point;
|
||||
|
||||
float reflectLen = sqrt(reflect_vec[0] * reflect_vec[0] + reflect_vec[1] * reflect_vec[1]);
|
||||
float cross = ab[0] * reflect_vec[1] - ab[1] * reflect_vec[0];
|
||||
/* Only if P is on the outside of the edge, which means the cross product is positive,
|
||||
* we consider this edge.
|
||||
*/
|
||||
bool valid = (cross > 0.0);
|
||||
|
||||
if (valid && (found_dist < 0 || reflectLen < found_dist)) {
|
||||
/* Stother_ab the info of the closest edge so far. */
|
||||
found_dist = reflectLen;
|
||||
found_t = t;
|
||||
found_edge = i + faces_[src_poly].start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_edge < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*r_dist_to_edge = found_dist;
|
||||
|
||||
/* Get the 'other' edge. I.E. the UV edge from the neighbor face. */
|
||||
int other_edge = loop_adjacency_map_[found_edge];
|
||||
|
||||
if (other_edge < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int dst_poly = loop_to_face_map_[other_edge];
|
||||
|
||||
if (r_other_poly) {
|
||||
*r_other_poly = dst_poly;
|
||||
}
|
||||
|
||||
int other_edge2 = other_edge + 1;
|
||||
if (other_edge2 >= faces_[dst_poly].start() + faces_[dst_poly].size()) {
|
||||
other_edge2 = faces_[dst_poly].start();
|
||||
}
|
||||
|
||||
float2 other_edgepoint1 = uv_to_xy(uv_map_[other_edge]);
|
||||
float2 other_edgepoint2 = uv_to_xy(uv_map_[other_edge2]);
|
||||
|
||||
/* Calculate the vector from the order edges last point to its first point. */
|
||||
float2 other_ab = other_edgepoint1 - other_edgepoint2;
|
||||
float2 other_reflect_point = other_edgepoint2 + (found_t * other_ab);
|
||||
float2 perpendicular_other_ab;
|
||||
perpendicular_other_ab.x = other_ab.y;
|
||||
perpendicular_other_ab.y = -other_ab.x;
|
||||
|
||||
/* The new point is dound_dist distance from other_reflect_point at a 90 degree angle to
|
||||
* other_ab */
|
||||
float2 new_point = other_reflect_point + (found_dist / math::length(perpendicular_other_ab)) *
|
||||
perpendicular_other_ab;
|
||||
|
||||
*r_destx = new_point.x;
|
||||
*r_desty = new_point.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
}; // class TextureMarginMap
|
||||
|
||||
const int TextureMarginMap::directions[8][2] = {
|
||||
{-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}};
|
||||
const int TextureMarginMap::distances[8] = {2, 3, 2, 3, 2, 3, 2, 3};
|
||||
|
||||
static void generate_margin(ImBuf *ibuf,
|
||||
char *mask,
|
||||
const int margin,
|
||||
const Span<float3> vert_positions,
|
||||
const int edges_num,
|
||||
const OffsetIndices<int> faces,
|
||||
const Span<int> corner_edges,
|
||||
const Span<int> corner_verts,
|
||||
const Span<float2> uv_map,
|
||||
const float uv_offset[2])
|
||||
{
|
||||
Array<int3> corner_tris(poly_to_tri_count(faces.size(), corner_edges.size()));
|
||||
bke::mesh::corner_tris_calc(vert_positions, faces, corner_verts, corner_tris);
|
||||
|
||||
Array<int> tri_faces(corner_tris.size());
|
||||
bke::mesh::corner_tris_calc_face_indices(faces, tri_faces);
|
||||
|
||||
TextureMarginMap map(ibuf->x, ibuf->y, uv_offset, edges_num, faces, corner_edges, uv_map);
|
||||
|
||||
bool draw_new_mask = false;
|
||||
/* Now the map contains 3 sorts of values: 0xFFFFFFFF for empty pixels, `0x80000000 + polyindex`
|
||||
* for margin pixels, just `polyindex` for face pixels. */
|
||||
if (mask) {
|
||||
mask = MEM_dupalloc(mask);
|
||||
}
|
||||
else {
|
||||
mask = MEM_new_array_zeroed<char>(size_t(ibuf->x) * size_t(ibuf->y), __func__);
|
||||
draw_new_mask = true;
|
||||
}
|
||||
|
||||
for (const int i : corner_tris.index_range()) {
|
||||
const int3 tri = corner_tris[i];
|
||||
float vec[3][2];
|
||||
|
||||
for (int a = 0; a < 3; a++) {
|
||||
const float *uv = uv_map[tri[a]];
|
||||
|
||||
/* NOTE(@ideasman42): workaround for pixel aligned UVs which are common and can screw up
|
||||
* our intersection tests where a pixel gets in between 2 faces or the middle of a quad,
|
||||
* camera aligned quads also have this problem but they are less common.
|
||||
* Add a small offset to the UVs, fixes bug #18685. */
|
||||
vec[a][0] = (uv[0] - uv_offset[0]) * float(ibuf->x) - (0.5f + 0.001f);
|
||||
vec[a][1] = (uv[1] - uv_offset[1]) * float(ibuf->y) - (0.5f + 0.002f);
|
||||
}
|
||||
|
||||
/* NOTE: we need the top bit for the dijkstra distance map. */
|
||||
BLI_assert(tri_faces[i] < 0x80000000);
|
||||
|
||||
map.rasterize_tri(vec[0], vec[1], vec[2], tri_faces[i], mask, draw_new_mask);
|
||||
}
|
||||
|
||||
char *tmpmask = MEM_dupalloc(mask);
|
||||
/* Extend (with averaging) by 2 pixels. Those will be overwritten, but it
|
||||
* helps linear interpolations on the edges of polygons. */
|
||||
IMB_filter_extend(ibuf, tmpmask, 2);
|
||||
MEM_delete(tmpmask);
|
||||
|
||||
map.grow_dijkstra(margin);
|
||||
|
||||
/* Looking further than 3 polygons away leads to so much cumulative rounding
|
||||
* that it isn't worth it. So hard-code it to 3. */
|
||||
map.lookup_pixels(ibuf, mask, 3);
|
||||
|
||||
/* Use the extend filter to fill in the missing pixels at the corners, not strictly correct, but
|
||||
* the visual difference seems very minimal. This also catches pixels we missed because of very
|
||||
* narrow polygons.
|
||||
*/
|
||||
IMB_filter_extend(ibuf, mask, margin);
|
||||
|
||||
MEM_delete(mask);
|
||||
}
|
||||
|
||||
} // namespace render::texturemargin
|
||||
|
||||
void RE_generate_texturemargin_adjacentfaces(ImBuf *ibuf,
|
||||
char *mask,
|
||||
const int margin,
|
||||
const Mesh *mesh,
|
||||
StringRef uv_layer,
|
||||
const float uv_offset[2])
|
||||
{
|
||||
const StringRef name = uv_layer.is_empty() ? mesh->active_uv_map_name() : uv_layer;
|
||||
const bke::AttributeAccessor attributes = mesh->attributes();
|
||||
const VArraySpan<float2> uv_map = *attributes.lookup<float2>(name, bke::AttrDomain::Corner);
|
||||
|
||||
render::texturemargin::generate_margin(ibuf,
|
||||
mask,
|
||||
margin,
|
||||
mesh->vert_positions(),
|
||||
mesh->edges_num,
|
||||
mesh->faces(),
|
||||
mesh->corner_edges(),
|
||||
mesh->corner_verts(),
|
||||
uv_map,
|
||||
uv_offset);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
1113
blender-5.2.0/source/blender/render/intern/texture_procedural.cc
Normal file
1113
blender-5.2.0/source/blender/render/intern/texture_procedural.cc
Normal file
File diff suppressed because it is too large
Load Diff
100
blender-5.2.0/source/blender/render/intern/tile_highlight.cc
Normal file
100
blender-5.2.0/source/blender/render/intern/tile_highlight.cc
Normal file
@@ -0,0 +1,100 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#include "tile_highlight.h"
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_rect.h"
|
||||
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
namespace blender::render {
|
||||
|
||||
TilesHighlight::Tile::Tile(const RenderResult *result) : rect(result->tilerect) {}
|
||||
|
||||
TilesHighlight::Tile::Tile(const int x, const int y, const int width, const int height)
|
||||
{
|
||||
BLI_rcti_init(&rect, x, x + width, y, y + height);
|
||||
}
|
||||
|
||||
uint64_t TilesHighlight::Tile::hash() const
|
||||
{
|
||||
return get_default_hash(rect.xmin, rect.xmax, rect.ymin, rect.ymax);
|
||||
}
|
||||
|
||||
void TilesHighlight::highlight_tile_for_result(const RenderResult *result)
|
||||
{
|
||||
const Tile tile(result);
|
||||
highlight_tile(tile);
|
||||
}
|
||||
|
||||
void TilesHighlight::unhighlight_tile_for_result(const RenderResult *result)
|
||||
{
|
||||
const Tile tile(result);
|
||||
unhighlight_tile(tile);
|
||||
}
|
||||
|
||||
void TilesHighlight::highlight_tile(const int x, const int y, const int width, const int height)
|
||||
{
|
||||
const Tile tile(x, y, width, height);
|
||||
highlight_tile(tile);
|
||||
}
|
||||
|
||||
void TilesHighlight::unhighlight_tile(const int x, const int y, const int width, const int height)
|
||||
{
|
||||
const Tile tile(x, y, width, height);
|
||||
unhighlight_tile(tile);
|
||||
}
|
||||
|
||||
void TilesHighlight::highlight_tile(const Tile &tile)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
highlighted_tiles_set_.add(tile);
|
||||
did_tiles_change_ = true;
|
||||
}
|
||||
|
||||
void TilesHighlight::unhighlight_tile(const Tile &tile)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
highlighted_tiles_set_.remove(tile);
|
||||
did_tiles_change_ = true;
|
||||
}
|
||||
|
||||
void TilesHighlight::clear()
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
highlighted_tiles_set_.clear();
|
||||
cached_highlighted_tiles_.clear_and_shrink();
|
||||
}
|
||||
|
||||
Span<rcti> TilesHighlight::get_all_highlighted_tiles() const
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
|
||||
/* Updated cached flat list if needed. */
|
||||
if (did_tiles_change_) {
|
||||
if (highlighted_tiles_set_.is_empty()) {
|
||||
cached_highlighted_tiles_.clear_and_shrink();
|
||||
}
|
||||
else {
|
||||
cached_highlighted_tiles_.reserve(highlighted_tiles_set_.size());
|
||||
for (const Tile &tile : highlighted_tiles_set_) {
|
||||
cached_highlighted_tiles_.append(tile.rect);
|
||||
}
|
||||
}
|
||||
|
||||
did_tiles_change_ = false;
|
||||
}
|
||||
|
||||
return cached_highlighted_tiles_;
|
||||
}
|
||||
|
||||
} // namespace blender::render
|
||||
70
blender-5.2.0/source/blender/render/intern/tile_highlight.h
Normal file
70
blender-5.2.0/source/blender/render/intern/tile_highlight.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct RenderResult;
|
||||
|
||||
namespace render {
|
||||
|
||||
class TilesHighlight {
|
||||
public:
|
||||
TilesHighlight() = default;
|
||||
~TilesHighlight() = default;
|
||||
|
||||
void highlight_tile_for_result(const RenderResult *result);
|
||||
void unhighlight_tile_for_result(const RenderResult *result);
|
||||
|
||||
void highlight_tile(int x, int y, int width, int height);
|
||||
void unhighlight_tile(int x, int y, int width, int height);
|
||||
|
||||
void clear();
|
||||
|
||||
Span<rcti> get_all_highlighted_tiles() const;
|
||||
|
||||
private:
|
||||
struct Tile {
|
||||
Tile() = default;
|
||||
explicit Tile(const RenderResult *result);
|
||||
explicit Tile(int x, int y, int width, int height);
|
||||
|
||||
uint64_t hash() const;
|
||||
|
||||
bool operator==(const Tile &other) const
|
||||
{
|
||||
return rect == other.rect;
|
||||
}
|
||||
bool operator!=(const Tile &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
rcti rect = {0, 0, 0, 0};
|
||||
};
|
||||
|
||||
void highlight_tile(const Tile &tile);
|
||||
void unhighlight_tile(const Tile &tile);
|
||||
|
||||
mutable Mutex mutex_;
|
||||
Set<Tile> highlighted_tiles_set_;
|
||||
|
||||
/* Cached flat list of currently highlighted tiles for a fast access via API. */
|
||||
mutable bool did_tiles_change_ = false;
|
||||
mutable Vector<rcti> cached_highlighted_tiles_;
|
||||
};
|
||||
|
||||
} // namespace render
|
||||
} // namespace blender
|
||||
243
blender-5.2.0/source/blender/render/intern/zbuf.cc
Normal file
243
blender-5.2.0/source/blender/render/intern/zbuf.cc
Normal file
@@ -0,0 +1,243 @@
|
||||
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*
|
||||
* \note Some of this logic has been duplicated in `COM_VectorBlurOperation.cc`
|
||||
* changes here may also apply also apply to that file.
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* Common includes */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
/* own includes */
|
||||
#include "zbuf.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* could enable at some point but for now there are far too many conversions */
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic ignored "-Wdouble-promotion"
|
||||
#endif
|
||||
|
||||
/* ****************** Spans ******************************* */
|
||||
|
||||
void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty)
|
||||
{
|
||||
memset(zspan, 0, sizeof(ZSpan));
|
||||
|
||||
zspan->rectx = rectx;
|
||||
zspan->recty = recty;
|
||||
|
||||
zspan->span1 = MEM_new_array_uninitialized<float>(recty, "zspan");
|
||||
zspan->span2 = MEM_new_array_uninitialized<float>(recty, "zspan");
|
||||
}
|
||||
|
||||
void zbuf_free_span(ZSpan *zspan)
|
||||
{
|
||||
if (zspan) {
|
||||
MEM_SAFE_DELETE(zspan->span1);
|
||||
MEM_SAFE_DELETE(zspan->span2);
|
||||
}
|
||||
}
|
||||
|
||||
/* reset range for clipping */
|
||||
static void zbuf_init_span(ZSpan *zspan)
|
||||
{
|
||||
zspan->miny1 = zspan->miny2 = zspan->recty + 1;
|
||||
zspan->maxy1 = zspan->maxy2 = -1;
|
||||
zspan->minp1 = zspan->maxp1 = zspan->minp2 = zspan->maxp2 = nullptr;
|
||||
}
|
||||
|
||||
static void zbuf_add_to_span(ZSpan *zspan, const float v1[2], const float v2[2])
|
||||
{
|
||||
const float *minv, *maxv;
|
||||
float *span;
|
||||
float xx1, dx0, xs0;
|
||||
int y, my0, my2;
|
||||
|
||||
if (v1[1] < v2[1]) {
|
||||
minv = v1;
|
||||
maxv = v2;
|
||||
}
|
||||
else {
|
||||
minv = v2;
|
||||
maxv = v1;
|
||||
}
|
||||
|
||||
my0 = ceil(minv[1]);
|
||||
my2 = floor(maxv[1]);
|
||||
|
||||
if (my2 < 0 || my0 >= zspan->recty) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* clip top */
|
||||
if (my2 >= zspan->recty) {
|
||||
my2 = zspan->recty - 1;
|
||||
}
|
||||
/* clip bottom */
|
||||
my0 = std::max(my0, 0);
|
||||
|
||||
if (my0 > my2) {
|
||||
return;
|
||||
}
|
||||
/* if (my0>my2) should still fill in, that way we get spans that skip nicely */
|
||||
|
||||
xx1 = maxv[1] - minv[1];
|
||||
if (xx1 > FLT_EPSILON) {
|
||||
dx0 = (minv[0] - maxv[0]) / xx1;
|
||||
xs0 = dx0 * (minv[1] - my2) + minv[0];
|
||||
}
|
||||
else {
|
||||
dx0 = 0.0f;
|
||||
xs0 = min_ff(minv[0], maxv[0]);
|
||||
}
|
||||
|
||||
/* empty span */
|
||||
if (zspan->maxp1 == nullptr) {
|
||||
span = zspan->span1;
|
||||
}
|
||||
else { /* does it complete left span? */
|
||||
if (maxv == zspan->minp1 || minv == zspan->maxp1) {
|
||||
span = zspan->span1;
|
||||
}
|
||||
else {
|
||||
span = zspan->span2;
|
||||
}
|
||||
}
|
||||
|
||||
if (span == zspan->span1) {
|
||||
// printf("left span my0 %d my2 %d\n", my0, my2);
|
||||
if (zspan->minp1 == nullptr || zspan->minp1[1] > minv[1]) {
|
||||
zspan->minp1 = minv;
|
||||
}
|
||||
if (zspan->maxp1 == nullptr || zspan->maxp1[1] < maxv[1]) {
|
||||
zspan->maxp1 = maxv;
|
||||
}
|
||||
zspan->miny1 = std::min(my0, zspan->miny1);
|
||||
zspan->maxy1 = std::max(my2, zspan->maxy1);
|
||||
}
|
||||
else {
|
||||
// printf("right span my0 %d my2 %d\n", my0, my2);
|
||||
if (zspan->minp2 == nullptr || zspan->minp2[1] > minv[1]) {
|
||||
zspan->minp2 = minv;
|
||||
}
|
||||
if (zspan->maxp2 == nullptr || zspan->maxp2[1] < maxv[1]) {
|
||||
zspan->maxp2 = maxv;
|
||||
}
|
||||
zspan->miny2 = std::min(my0, zspan->miny2);
|
||||
zspan->maxy2 = std::max(my2, zspan->maxy2);
|
||||
}
|
||||
|
||||
for (y = my2; y >= my0; y--, xs0 += dx0) {
|
||||
/* xs0 is the X-coordinate! */
|
||||
span[y] = xs0;
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------*/
|
||||
/* Functions */
|
||||
/*-----------------------------------------------------------*/
|
||||
|
||||
void zspan_scanconvert(ZSpan *zspan,
|
||||
void *handle,
|
||||
float *v1,
|
||||
float *v2,
|
||||
float *v3,
|
||||
void (*func)(void *, int, int, float, float))
|
||||
{
|
||||
float x0, y0, x1, y1, x2, y2, z0, z1, z2;
|
||||
float u, v, uxd, uyd, vxd, vyd, uy0, vy0, xx1;
|
||||
const float *span1, *span2;
|
||||
int i, j, x, y, sn1, sn2, rectx = zspan->rectx, my0, my2;
|
||||
|
||||
/* init */
|
||||
zbuf_init_span(zspan);
|
||||
|
||||
/* set spans */
|
||||
zbuf_add_to_span(zspan, v1, v2);
|
||||
zbuf_add_to_span(zspan, v2, v3);
|
||||
zbuf_add_to_span(zspan, v3, v1);
|
||||
|
||||
/* clipped */
|
||||
if (zspan->minp2 == nullptr || zspan->maxp2 == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
my0 = max_ii(zspan->miny1, zspan->miny2);
|
||||
my2 = min_ii(zspan->maxy1, zspan->maxy2);
|
||||
|
||||
// printf("my %d %d\n", my0, my2);
|
||||
if (my2 < my0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* ZBUF DX DY, in floats still */
|
||||
x1 = v1[0] - v2[0];
|
||||
x2 = v2[0] - v3[0];
|
||||
y1 = v1[1] - v2[1];
|
||||
y2 = v2[1] - v3[1];
|
||||
|
||||
z1 = 1.0f; /* (u1 - u2) */
|
||||
z2 = 0.0f; /* (u2 - u3) */
|
||||
|
||||
x0 = y1 * z2 - z1 * y2;
|
||||
y0 = z1 * x2 - x1 * z2;
|
||||
z0 = x1 * y2 - y1 * x2;
|
||||
|
||||
if (z0 == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
xx1 = (x0 * v1[0] + y0 * v1[1]) / z0 + 1.0f;
|
||||
uxd = -double(x0) / double(z0);
|
||||
uyd = -double(y0) / double(z0);
|
||||
uy0 = double(my2) * uyd + double(xx1);
|
||||
|
||||
z1 = -1.0f; /* (v1 - v2) */
|
||||
z2 = 1.0f; /* (v2 - v3) */
|
||||
|
||||
x0 = y1 * z2 - z1 * y2;
|
||||
y0 = z1 * x2 - x1 * z2;
|
||||
|
||||
xx1 = (x0 * v1[0] + y0 * v1[1]) / z0;
|
||||
vxd = -double(x0) / double(z0);
|
||||
vyd = -double(y0) / double(z0);
|
||||
vy0 = double(my2) * vyd + double(xx1);
|
||||
|
||||
/* correct span */
|
||||
span1 = zspan->span1 + my2;
|
||||
span2 = zspan->span2 + my2;
|
||||
|
||||
for (i = 0, y = my2; y >= my0; i++, y--, span1--, span2--) {
|
||||
|
||||
sn1 = floor(min_ff(*span1, *span2));
|
||||
sn2 = floor(max_ff(*span1, *span2));
|
||||
sn1++;
|
||||
|
||||
if (sn2 >= rectx) {
|
||||
sn2 = rectx - 1;
|
||||
}
|
||||
sn1 = std::max(sn1, 0);
|
||||
|
||||
u = ((double(sn1) * uxd) + uy0) - (i * uyd);
|
||||
v = ((double(sn1) * vxd) + vy0) - (i * vyd);
|
||||
|
||||
for (j = 0, x = sn1; x <= sn2; j++, x++) {
|
||||
func(handle, x, y, u + (j * uxd), v + (j * vxd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
39
blender-5.2.0/source/blender/render/intern/zbuf.h
Normal file
39
blender-5.2.0/source/blender/render/intern/zbuf.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup render
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** Span fill in method, is also used to localize data for Z-buffering. */
|
||||
struct ZSpan {
|
||||
int rectx, recty; /* range for clipping */
|
||||
|
||||
int miny1, maxy1, miny2, maxy2; /* actual filled in range */
|
||||
const float *minp1, *maxp1, *minp2, *maxp2; /* vertex pointers detect min/max range in */
|
||||
float *span1, *span2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Each Z-buffer has coordinates transformed to local rect coordinates, so we can simply clip.
|
||||
*/
|
||||
void zbuf_alloc_span(struct ZSpan *zspan, int rectx, int recty);
|
||||
void zbuf_free_span(struct ZSpan *zspan);
|
||||
|
||||
/**
|
||||
* Scan-convert for strand triangles, calls function for each x, y coordinate
|
||||
* and gives UV barycentrics and z.
|
||||
*/
|
||||
void zspan_scanconvert(struct ZSpan *zspan,
|
||||
void *handle,
|
||||
float *v1,
|
||||
float *v2,
|
||||
float *v3,
|
||||
void (*func)(void *, int, int, float, float));
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user