From df0d0fc18d916a2849653883b6ad06583c64a378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Wed, 19 Feb 2025 08:07:01 +0100 Subject: [PATCH] hairparticle: reduced buffer creation, optimizations --- .../shaders/ShaderInterop_HairParticle.h | 4 +- WickedEngine/shaders/globals.hlsli | 4 + .../shaders/hairparticle_simulateCS.hlsl | 115 +++++++++++------- WickedEngine/wiHairParticle.cpp | 72 +++-------- WickedEngine/wiHairParticle.h | 2 +- WickedEngine/wiScene.cpp | 2 +- WickedEngine/wiTerrain.cpp | 1 + WickedEngine/wiVersion.cpp | 2 +- 8 files changed, 102 insertions(+), 100 deletions(-) diff --git a/WickedEngine/shaders/ShaderInterop_HairParticle.h b/WickedEngine/shaders/ShaderInterop_HairParticle.h index 833d4135f4..acc036a5b6 100644 --- a/WickedEngine/shaders/ShaderInterop_HairParticle.h +++ b/WickedEngine/shaders/ShaderInterop_HairParticle.h @@ -6,10 +6,12 @@ #define THREADCOUNT_SIMULATEHAIR 64 -struct PatchSimulationData +struct alignas(16) PatchSimulationData { float3 prevTail; + float padding0; float3 currentTail; + float padding1; }; enum HAIR_FLAGS diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index cf36fe2d95..c3c5b197db 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -904,7 +904,11 @@ inline void draw_line(float3 a, float3 b, float4 color = 1) debugline.posB = float4(b, 1); debugline.colorA = color; debugline.colorB = color; +#ifdef __PSSL__ + indirect_debug_buffer.TypedStore(sizeof(IndirectDrawArgsInstanced) + sizeof(DebugLine) * prevCount / 2, debugline); +#else indirect_debug_buffer.Store(sizeof(IndirectDrawArgsInstanced) + sizeof(DebugLine) * prevCount / 2, debugline); +#endif // __PSSL__ } inline void draw_point(float3 pos, float size = 1, float4 color = 1) { diff --git a/WickedEngine/shaders/hairparticle_simulateCS.hlsl b/WickedEngine/shaders/hairparticle_simulateCS.hlsl index 9a348d3f61..cd9baa6c32 100644 --- a/WickedEngine/shaders/hairparticle_simulateCS.hlsl +++ b/WickedEngine/shaders/hairparticle_simulateCS.hlsl @@ -2,14 +2,14 @@ #include "hairparticleHF.hlsli" #include "ShaderInterop_HairParticle.h" -static const float3 HAIRPATCH[] = { +static const half3 HAIRPATCH[] = { // root (for every strand): - float3(-1, -1, 0), - float3(1, -1, 0), + half3(-1, -1, 0), + half3(1, -1, 0), // cap (for every segment of every strand): - float3(-1, 1, 0), - float3(1, 1, 0), + half3(-1, 1, 0), + half3(1, 1, 0), }; Buffer meshIndexBuffer : register(t0); @@ -24,12 +24,15 @@ RWBuffer culledIndexBuffer : register(u3); RWStructuredBuffer indirectBuffer : register(u4); RWBuffer vertexBuffer_POS_RT : register(u5); RWBuffer vertexBuffer_NOR : register(u6); +RWBuffer primitiveBuffer : register(u7); [numthreads(THREADCOUNT_SIMULATEHAIR, 1, 1)] void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIndex : SV_GroupIndex) { if (DTid.x >= xHairStrandCount) return; + + const bool regenerate_frame = xHairFlags & HAIR_FLAG_REGENERATE_FRAME; ShaderGeometry geometry = HairGetGeometry(); @@ -73,7 +76,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn float3 position = attribute_at_bary(pos0, pos1, pos2, bary); position = mul(xHairBaseMeshUnormRemap.GetMatrix(), float4(position, 1)).xyz; half3 target = normalize(attribute_at_bary(nor0, nor1, nor2, bary)); - target = normalize(mul((half3x3)worldMatrix, target)); + target = normalize(mul(xHairTransform.GetMatrixAdjoint(), target)); half3 tangent = normalize(mul(half3(hemispherepoint_cos(rng.next_float(), rng.next_float()).xy, 0), get_tangentspace(target))); half3 binormal = cross(target, tangent); half strand_length = attribute_at_bary(length0, length1, length2, bary); @@ -96,16 +99,20 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn len *= atlas_rect.size; len /= (half)xHairSegmentCount; const float2 frame = float2(atlas_rect.aspect * xHairAspect * xHairSegmentCount, 1) * len * 0.5; + const float segment_radius = max(frame.x, frame.y); const uint gfx_vertexcount_per_strand = (xHairSegmentCount * 2 + 2) * xHairBillboardCount; const uint gfx_indexcount_per_particle = 6 * xHairBillboardCount; - uint v0 = DTid.x * gfx_vertexcount_per_strand; + const uint gfx_indexcount_per_strand = gfx_indexcount_per_particle * xHairSegmentCount; + const uint index0 = DTid.x * gfx_indexcount_per_strand; + const uint vertexID0 = DTid.x * gfx_vertexcount_per_strand; + uint v0 = vertexID0; //draw_line(base, base + tangent, float4(1, 0, 0, 1)); //draw_line(base, base + target, float4(0, 1, 0, 1)); //draw_line(base, base + binormal, float4(0, 0, 1, 1)); - float3 bend = 0; + half3 bend = 0; if (xHairFlags & HAIR_FLAG_CAMERA_BEND) { // Bend down to camera up vector to avoid seeing flat planes from above @@ -117,19 +124,20 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn rng.init(uint2(xHairRandomSeed, DTid.x), 1); // reinit random for consistent billboard variation! for (uint billboardID = 0; billboardID < xHairBillboardCount; ++billboardID) { - float siz = billboardID == 0 ? 1 : lerp(0.2, 1, rng.next_float()); - float rot = billboardID == 0 ? 0 : (rng.next_float() * PI); - float2 rot_sincos; + half siz = billboardID == 0 ? 1 : lerp(0.2, 1, rng.next_float()); + half rot = billboardID == 0 ? 0 : (rng.next_float() * PI); + half2 rot_sincos; sincos(rot, rot_sincos.x, rot_sincos.y); - float3x3 variationMatrix = float3x3( + half3x3 variationMatrix = half3x3( rot_sincos.y * siz, 0, -rot_sincos.x, 0, siz, 0, rot_sincos.x, 0, rot_sincos.y * siz ); + variationMatrix = mul(variationMatrix, TBN); for (uint vertexID = 0; vertexID < 2; ++vertexID) { - float3 patchPos = HAIRPATCH[vertexID]; + half3 patchPos = HAIRPATCH[vertexID]; float2 uv = patchPos.xy; uv = uv * float2(0.5, 0.5) + 0.5; uv.y = 1 - uv.y; @@ -144,9 +152,6 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn // variation based on billboardID: patchPos = mul(patchPos, variationMatrix); - // rotate the patch into the tangent space of the emitting triangle: - patchPos = mul(patchPos, TBN); - float3 position = base + patchPos; if (xHairFlags & HAIR_FLAG_UNORM_POS) @@ -168,20 +173,20 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn } } - const float dt = clamp(GetFrame().delta_time, 0, 1.0 / 30.0); // clamp delta time to avoid simulation blowing up + const half dt = clamp(GetFrame().delta_time, 0, 1.0 / 30.0); // clamp delta time to avoid simulation blowing up - const float gravityPower = xHairGravityPower; - const float stiffnessForce = xHairStiffness; - const float dragForce = xHairDrag; - const float3 boneAxis = target; - const float boneLength = len; + const half gravityPower = xHairGravityPower; + const half stiffnessForce = xHairStiffness; + const half dragForce = xHairDrag; + const half3 boneAxis = target; + const half boneLength = len; - for (uint segmentID = 0; segmentID < xHairSegmentCount; ++segmentID) + for (uint segmentID = 0; !distance_culled && (segmentID < xHairSegmentCount); ++segmentID) { // Identifies the hair strand segment particle: const uint particleID = strandID + segmentID; - if (xHairFlags & HAIR_FLAG_REGENERATE_FRAME) + if (regenerate_frame) { float3 tail = base + boneAxis * boneLength; simulationBuffer[particleID].prevTail = tail; @@ -190,19 +195,19 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn float3 tail_current = simulationBuffer[particleID].currentTail; float3 tail_prev = simulationBuffer[particleID].prevTail; - float3 inertia = (tail_current - tail_prev) * (1 - dragForce); - float3 stiffness = boneAxis * stiffnessForce; - float3 external = gravityPower * float3(0, -1, 0); - float3 wind = sample_wind(tail_current, ((float)segmentID + 1) / (float)xHairSegmentCount); + half3 inertia = (tail_current - tail_prev) * (1 - dragForce); + half3 stiffness = boneAxis * stiffnessForce; + half3 external = gravityPower * float3(0, -1, 0); + half3 wind = sample_wind(tail_current, ((float)segmentID + 1) / (float)xHairSegmentCount); external += wind; float3 tail_next = tail_current + inertia + dt * (stiffness + external); - float3 to_tail = normalize(tail_next - base); + half3 to_tail = normalize(tail_next - base); tail_next = base + to_tail * boneLength; //draw_sphere(tail_next, len); - // Accumulate forces, apply colliders: + // Apply every force and collider: for (uint i = forces().first_item(); i < forces().end_item(); ++i) { ShaderEntity entity = load_entity(i); @@ -217,7 +222,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn { float3 A = entity.position; float3 B = entity.GetColliderTip(); - float3 N = normalize(A - B); + half3 N = normalize(A - B); A -= N * range; B += N * range; //if (DTid.x == 0) @@ -229,7 +234,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn float3 dir = C - tail_next; float dist = length(dir); dir /= dist; - dist = dist - range - len * 0.5; + dist = dist - range - segment_radius; if (dist < 0) { tail_next += dir * dist; @@ -257,7 +262,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn tail_next = base + to_tail * boneLength; break; case ENTITY_TYPE_COLLIDER_SPHERE: - dist = dist - range - len; + dist = dist - range - segment_radius; if (dist < 0) { tail_next += dir * dist; @@ -273,7 +278,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn dir *= -1; dist = abs(dist); } - dist = dist - len; + dist = dist - segment_radius; if (dist < 0) { float4x4 planeProjection = load_entitymatrix(entity.GetMatrixIndex()); @@ -325,19 +330,20 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn rng.init(uint2(xHairRandomSeed, DTid.x), 1); // reinit random for consistent billboard variation! for(uint billboardID = 0; billboardID < xHairBillboardCount; ++billboardID) { - float siz = billboardID == 0 ? 1 : lerp(0.2, 1, rng.next_float()); - float rot = billboardID == 0 ? 0 : (rng.next_float() * PI); - float2 rot_sincos; + half siz = billboardID == 0 ? 1 : lerp(0.2, 1, rng.next_float()); + half rot = billboardID == 0 ? 0 : (rng.next_float() * PI); + half2 rot_sincos; sincos(rot, rot_sincos.x, rot_sincos.y); - float3x3 variationMatrix = float3x3( + half3x3 variationMatrix = half3x3( rot_sincos.y * siz, 0, -rot_sincos.x, 0, siz, 0, rot_sincos.x, 0, rot_sincos.y * siz ); + variationMatrix = mul(variationMatrix, TBN); for (uint vertexID = 2; vertexID < 4; ++vertexID) { - float3 patchPos = HAIRPATCH[vertexID]; + half3 patchPos = HAIRPATCH[vertexID]; float2 uv = patchPos.xy; uv = uv * float2(0.5, 0.5) + 0.5; uv.y = lerp((float)segmentID / (float)xHairSegmentCount, ((float)segmentID + 1) / (float)xHairSegmentCount, uv.y); @@ -353,9 +359,6 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn // variation based on billboardID: patchPos = mul(patchPos, variationMatrix); - // rotate the patch into the tangent space of the emitting triangle: - patchPos = mul(patchPos, TBN); - float3 position = base + patchPos; if (xHairFlags & HAIR_FLAG_UNORM_POS) @@ -380,7 +383,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn // Frustum culling: ShaderSphere sphere; sphere.center = (base + tail_next) * 0.5; - sphere.radius = len; + sphere.radius = segment_radius; + //draw_sphere(sphere.center, sphere.radius); const bool visible = !distance_culled && GetCamera().frustum.intersects(sphere); @@ -413,4 +417,29 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn base = tail_next; target = normal; } + + // Primitive buffer creation is done here instead of CPU to reduce CPU time spent in buffer creations: + if (regenerate_frame) + { + uint i = index0; + v0 = vertexID0; + uint rootOffset = v0; + uint capOffset = rootOffset + 2 * xHairBillboardCount; + for (uint billboardID = 0; billboardID < xHairBillboardCount; ++billboardID) + { + for (uint segmentID = 0; segmentID < xHairSegmentCount; ++segmentID) + { + primitiveBuffer[i++] = rootOffset + 0; + primitiveBuffer[i++] = rootOffset + 1; + primitiveBuffer[i++] = capOffset + 0; + primitiveBuffer[i++] = capOffset + 0; + primitiveBuffer[i++] = rootOffset + 1; + primitiveBuffer[i++] = capOffset + 1; + rootOffset += 2; + capOffset += 2; + v0 += 2; + } + v0 += 2; + } + } } diff --git a/WickedEngine/wiHairParticle.cpp b/WickedEngine/wiHairParticle.cpp index ea547612d5..976b50134e 100644 --- a/WickedEngine/wiHairParticle.cpp +++ b/WickedEngine/wiHairParticle.cpp @@ -114,6 +114,7 @@ namespace wi const size_t position_stride = GetFormatStride(position_format); const Format ib_format = GetIndexBufferFormatRaw(gfx_vertexcount); + const uint32_t ib_stride = GetFormatStride(ib_format); const uint64_t alignment = device->GetMinOffsetAlignment(&bd) * sizeof(IndirectDrawArgsIndexedInstanced) * // additional alignment @@ -126,7 +127,8 @@ namespace wi vb_nor.size = sizeof(MeshComponent::Vertex_NOR) * gfx_vertexcount; vb_uvs.size = sizeof(MeshComponent::Vertex_UVS) * gfx_vertexcount; wetmap.size = sizeof(uint16_t) * gfx_vertexcount; - ib_culled.size = GetFormatStride(ib_format) * gfx_indexcount; + ib_culled.size = ib_stride * gfx_indexcount; + prim_view.size = ib_stride * gfx_indexcount; indirect_view.size = sizeof(IndirectDrawArgsIndexedInstanced); vb_pos_raytracing.size = position_stride * gfx_vertexcount; @@ -139,6 +141,7 @@ namespace wi AlignTo(vb_uvs.size, alignment) + AlignTo(wetmap.size, alignment) + AlignTo(ib_culled.size, alignment) + + AlignTo(prim_view.size, alignment) + AlignTo(vb_pos_raytracing.size, alignment) ; device->CreateBuffer(&bd, nullptr, &generalBuffer); @@ -214,6 +217,14 @@ namespace wi ib_culled.descriptor_uav = device->GetDescriptorIndex(&generalBuffer, SubresourceType::UAV, ib_culled.subresource_uav); buffer_offset += ib_culled.size; + buffer_offset = AlignTo(buffer_offset, alignment); + prim_view.offset = buffer_offset; + prim_view.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, prim_view.offset, prim_view.size, &ib_format); + prim_view.subresource_uav = device->CreateSubresource(&generalBuffer, SubresourceType::UAV, prim_view.offset, prim_view.size, &ib_format); + prim_view.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, prim_view.subresource_srv); + prim_view.descriptor_uav = device->GetDescriptorIndex(&generalBuffer, SubresourceType::UAV, prim_view.subresource_uav); + buffer_offset += prim_view.size; + buffer_offset = AlignTo(buffer_offset, alignment); vb_pos_raytracing.offset = buffer_offset; vb_pos_raytracing.subresource_uav = device->CreateSubresource(&generalBuffer, SubresourceType::UAV, vb_pos_raytracing.offset, vb_pos_raytracing.size, &position_format); @@ -222,51 +233,6 @@ namespace wi } - if (gfx_indexcount > 0 && primitiveBuffer.desc.size != gfx_indexcount * GetFormatStride(Format::R32_UINT)) - { - GPUBufferDesc bd; - bd.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::INDEX_BUFFER; - if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING)) - { - bd.misc_flags |= ResourceMiscFlag::RAY_TRACING; - } - bd.format = Format::R32_UINT; - bd.stride = GetFormatStride(bd.format); - bd.size = bd.stride * gfx_indexcount; - auto fill_ib = [&](void* dst) - { - uint32_t* primitiveData = (uint32_t*)dst; - uint32_t i = 0; - uint32_t v0 = 0; - for (uint32_t strandID = 0; strandID < strandCount; ++strandID) - { - uint32_t rootOffset = v0; - uint32_t capOffset = rootOffset + 2 * billboardCount; - for (uint32_t billboardID = 0; billboardID < billboardCount; ++billboardID) - { - for (uint32_t segmentID = 0; segmentID < segmentCount; ++segmentID) - { - primitiveData[i++] = rootOffset + 0; - primitiveData[i++] = rootOffset + 1; - primitiveData[i++] = capOffset + 0; - primitiveData[i++] = capOffset + 0; - primitiveData[i++] = rootOffset + 1; - primitiveData[i++] = capOffset + 1; - rootOffset += 2; - capOffset += 2; - v0 += 2; - } - v0 += 2; - } - } - assert(v0 == gfx_vertexcount); - assert(i == gfx_indexcount); - }; - device->CreateBuffer2(&bd, fill_ib, &primitiveBuffer); - device->SetName(&primitiveBuffer, "HairParticleSystem::primitiveBuffer"); - } - - GPUBufferDesc bd; bd.usage = Usage::DEFAULT; bd.size = sizeof(HairParticleCB); @@ -275,7 +241,6 @@ namespace wi device->CreateBuffer(&bd, nullptr, &constantBuffer); device->SetName(&constantBuffer, "HairParticleSystem::constantBuffer"); - if (!vertex_lengths.empty()) { auto pack_lengths = [&](void* dest) { @@ -309,7 +274,7 @@ namespace wi { GraphicsDevice* device = wi::graphics::GetDevice(); - if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING) && primitiveBuffer.IsValid()) + if (device->CheckCapability(GraphicsDeviceCapability::RAYTRACING) && prim_view.IsValid()) { RaytracingAccelerationStructureDesc desc; desc.type = RaytracingAccelerationStructureDesc::Type::BOTTOMLEVEL; @@ -320,10 +285,10 @@ namespace wi auto& geometry = desc.bottom_level.geometries.back(); geometry.type = RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::TRIANGLES; geometry.triangles.vertex_buffer = generalBuffer; - geometry.triangles.index_buffer = primitiveBuffer; - geometry.triangles.index_format = GetIndexBufferFormat(primitiveBuffer.desc.format); + geometry.triangles.index_buffer = generalBuffer; + geometry.triangles.index_format = GetIndexBufferFormat(GetVertexCount()); geometry.triangles.index_count = GetIndexCount(); - geometry.triangles.index_offset = 0; + geometry.triangles.index_offset = prim_view.offset / GetFormatStride(GetIndexBufferFormatRaw(GetVertexCount())); geometry.triangles.vertex_count = (uint32_t)(vb_pos_raytracing.size / GetFormatStride(position_format)); geometry.triangles.vertex_format = position_format == Format::R32G32B32A32_FLOAT ? Format::R32G32B32_FLOAT : position_format; geometry.triangles.vertex_stride = GetFormatStride(position_format); @@ -374,7 +339,7 @@ namespace wi (_flags & REBUILD_BUFFERS) || !constantBuffer.IsValid() || particleCount != simulation_view.size / sizeof(PatchSimulationData) || - (gfx_indexcount > 0 && primitiveBuffer.desc.size != gfx_indexcount * GetFormatStride(Format::R32_UINT)) + (gfx_indexcount > 0 && prim_view.size != gfx_indexcount * GetFormatStride(GetIndexBufferFormatRaw(GetVertexCount()))) ) { CreateRenderData(); @@ -507,6 +472,7 @@ namespace wi device->BindUAV(&hair.generalBuffer, 4, cmd, hair.indirect_view.subresource_uav); device->BindUAV(&hair.generalBuffer, 5, cmd, hair.vb_pos_raytracing.subresource_uav); device->BindUAV(&hair.generalBuffer, 6, cmd, hair.vb_nor.subresource_uav); + device->BindUAV(&hair.generalBuffer, 7, cmd, hair.prim_view.subresource_uav); if (hair.indexBuffer.IsValid()) { @@ -584,7 +550,7 @@ namespace wi } device->BindConstantBuffer(&constantBuffer, CB_GETBINDSLOT(HairParticleCB), cmd); - device->BindResource(&primitiveBuffer, 0, cmd); + device->BindResource(&generalBuffer, 0, cmd, prim_view.subresource_srv); device->BindIndexBuffer(&generalBuffer, GetIndexBufferFormat(GetVertexCount()), ib_culled.offset, cmd); diff --git a/WickedEngine/wiHairParticle.h b/WickedEngine/wiHairParticle.h index 2cc6622cc3..de0031ecaa 100644 --- a/WickedEngine/wiHairParticle.h +++ b/WickedEngine/wiHairParticle.h @@ -29,7 +29,7 @@ namespace wi wi::scene::MeshComponent::BufferView wetmap; wi::scene::MeshComponent::BufferView ib_culled; wi::scene::MeshComponent::BufferView indirect_view; - wi::graphics::GPUBuffer primitiveBuffer; + wi::scene::MeshComponent::BufferView prim_view; wi::graphics::GPUBuffer indexBuffer; wi::graphics::GPUBuffer vertexBuffer_length; diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index 96dc8d00e1..7e1ca4d49f 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -4892,7 +4892,7 @@ namespace wi::scene geometry.indexOffset = 0; geometry.indexCount = indexCount; geometry.materialIndex = (uint)materials.GetIndex(entity); - geometry.ib = device->GetDescriptorIndex(&hair.primitiveBuffer, SubresourceType::SRV); + geometry.ib = hair.prim_view.descriptor_srv; geometry.vb_pos_wind = hair.vb_pos[0].descriptor_srv; geometry.vb_nor = hair.vb_nor.descriptor_srv; geometry.vb_pre = hair.vb_pos[1].descriptor_srv; diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp index 211d6f0c6d..76882d625f 100644 --- a/WickedEngine/wiTerrain.cpp +++ b/WickedEngine/wiTerrain.cpp @@ -855,6 +855,7 @@ namespace wi::terrain } // Start the generation on a background thread and keep it running until the next frame + generator->workload.priority = wi::jobsystem::Priority::Low; wi::jobsystem::Execute(generator->workload, [=](wi::jobsystem::JobArgs a) { wi::Timer timer; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 6bc4fe63bb..84d25e501f 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 683; + const int revision = 684; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);