* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "sampler.h"
#include <scene/ext/intf_ecs_context.h>
#include <scene/ext/intf_render_resource.h>
#include <3d/ecs/components/render_handle_component.h>
#include <render/device/intf_gpu_resource_manager.h>
SCENE_BEGIN_NAMESPACE()
static constexpr BASE_NS::string_view MAG_FILTER_PROPERTY = "MagFilter";
static constexpr BASE_NS::string_view MIN_FILTER_PROPERTY = "MinFilter";
static constexpr BASE_NS::string_view MIP_MAP_MODE_PROPERTY = "MipMapMode";
static constexpr BASE_NS::string_view ADDRESS_MODE_U_PROPERTY = "AddressModeU";
static constexpr BASE_NS::string_view ADDRESS_MODE_V_PROPERTY = "AddressModeV";
static constexpr BASE_NS::string_view ADDRESS_MODE_W_PROPERTY = "AddressModeW";
static constexpr BASE_NS::string_view DEFAULT_SAMPLER_NAME = "CORE_DEFAULT_SAMPLER_LINEAR_MIPMAP_REPEAT";
template <typename T, typename U>
bool SetSamplerValue(META_NS::IProperty* p, const U& f, bool setToDefault = false)
{
if (p) {
auto value = META_NS::Any<T>(static_cast<T>(f));
if (!setToDefault) {
p->SetValue(value);
} else if (auto sp = interface_cast<META_NS::IStackProperty>(p)) {
sp->SetDefaultValue(value);
}
return true;
}
return false;
}
template <typename T, typename U>
bool InitializeSamplerValue(META_NS::IProperty* p, const U& value, const U& defaultValue, bool setValue)
{
auto success = SetSamplerValue<T, U>(p, defaultValue, true);
if (setValue) {
success &= SetSamplerValue<T, U>(p, value, false);
}
return success;
}
template <typename T, typename U>
bool GetSamplerValue(const META_NS::IProperty* p, U& value)
{
if (p && p->IsValueSet()) {
T v{};
p->GetValue().GetValue(v);
if (value != static_cast<U>(v)) {
value = static_cast<U>(v);
return true;
}
}
return false;
}
bool ResetSamplerProperty(META_NS::IProperty* p)
{
if (p && p->IsValueSet()) {
p->ResetValue();
return true;
}
return false;
}
META_NS::IProperty* Sampler::GetExistingProperty(BASE_NS::string_view name)
{
return GetProperty(name, META_NS::MetadataQuery::EXISTING).get();
}
const META_NS::IProperty* Sampler::GetExistingProperty(BASE_NS::string_view name) const
{
return GetProperty(name, META_NS::MetadataQuery::EXISTING).get();
}
void Sampler::NotifyChanged()
{
if (CanNotifyChanged()) {
sampler_ = {};
if (auto ev = EventOnResourceChanged(META_NS::MetadataQuery::EXISTING)) {
META_NS::Invoke<META_NS::IOnChanged>(ev);
}
}
}
bool Sampler::CanNotifyChanged()
{
CORE_NS::UniqueLock lock(transaction_);
if (!setting_) {
return true;
}
changedDuringTransaction_ = true;
return false;
}
void Sampler::StartTransaction()
{
CORE_NS::UniqueLock lock(transaction_);
setting_++;
}
void Sampler::EndTransaction(bool changed)
{
bool shouldNotify = false;
{
CORE_NS::UniqueLock lock(transaction_);
setting_--;
if (!setting_) {
shouldNotify = changed && changedDuringTransaction_;
changedDuringTransaction_ = false;
}
}
if (shouldNotify) {
NotifyChanged();
}
}
void Sampler::ResetObject()
{
auto task = [&]() -> bool {
StartTransaction();
bool changed = ResetSamplerProperty(GetExistingProperty(MAG_FILTER_PROPERTY));
changed |= ResetSamplerProperty(GetExistingProperty(MIN_FILTER_PROPERTY));
changed |= ResetSamplerProperty(GetExistingProperty(MIP_MAP_MODE_PROPERTY));
changed |= ResetSamplerProperty(GetExistingProperty(ADDRESS_MODE_U_PROPERTY));
changed |= ResetSamplerProperty(GetExistingProperty(ADDRESS_MODE_V_PROPERTY));
changed |= ResetSamplerProperty(GetExistingProperty(ADDRESS_MODE_W_PROPERTY));
EndTransaction(false);
return changed;
};
auto scene = scene_.lock();
auto changed = scene ? scene->RunDirectlyOrInTask(task) : task();
if (changed) {
NotifyChanged();
}
}
void Sampler::SetScene(const IInternalScene::Ptr& scene)
{
scene_ = scene;
}
void Sampler::OnPropertyChanged(const META_NS::IProperty&)
{
NotifyChanged();
}
void Sampler::OnMetadataConstructed(const META_NS::StaticMetadata& m, CORE_NS::IInterface& i)
{
if (!sampler_.handle || !scene_.lock()) {
return;
}
if (auto p = interface_cast<META_NS::IProperty>(&i)) {
const auto& desc = sampler_.descriptor;
const auto& defaultDesc = sampler_.isDefault ? sampler_.descriptor : GetDefaultSampler().descriptor;
const auto usingDefault = sampler_.isDefault;
BASE_NS::string_view name(m.name);
if (name == MAG_FILTER_PROPERTY) {
InitializeSamplerValue<SamplerFilter>(p, desc.magFilter, defaultDesc.magFilter, !usingDefault);
} else if (name == MIN_FILTER_PROPERTY) {
InitializeSamplerValue<SamplerFilter>(p, desc.minFilter, defaultDesc.minFilter, !usingDefault);
} else if (name == MIP_MAP_MODE_PROPERTY) {
InitializeSamplerValue<SamplerFilter>(p, desc.mipMapMode, defaultDesc.mipMapMode, !usingDefault);
} else if (name == ADDRESS_MODE_U_PROPERTY) {
InitializeSamplerValue<SamplerAddressMode>(p, desc.addressModeU, defaultDesc.addressModeU, !usingDefault);
} else if (name == ADDRESS_MODE_V_PROPERTY) {
InitializeSamplerValue<SamplerAddressMode>(p, desc.addressModeV, defaultDesc.addressModeV, !usingDefault);
} else if (name == ADDRESS_MODE_W_PROPERTY) {
InitializeSamplerValue<SamplerAddressMode>(p, desc.addressModeW, defaultDesc.addressModeW, !usingDefault);
}
}
}
Sampler::SamplerInfo Sampler::GetDefaultSampler() const
{
SamplerInfo info;
if (auto scene = scene_.lock()) {
auto& mgr = scene->GetRenderContext().GetDevice().GetGpuResourceManager();
info.handle = mgr.GetSamplerHandle(DEFAULT_SAMPLER_NAME);
info.descriptor = mgr.GetSamplerDescriptor(info.handle);
info.isDefault = true;
}
return info;
}
RENDER_NS::RenderHandleReference Sampler::GetSamplerHandle(const SamplerInfo& info) const noexcept
{
return info.isDefault ? RENDER_NS::RenderHandleReference{} : info.handle;
}
bool Sampler::UpdateSamplerFromHandle(
RENDER_NS::IRenderContext& context, const RENDER_NS::RenderHandleReference& handle)
{
if (sampler_.handle && handle.GetHandle() == sampler_.handle.GetHandle()) {
return false;
}
if (handle) {
if (handle.GetHandleType() != RENDER_NS::RenderHandleType::GPU_SAMPLER) {
CORE_LOG_E("Sampler requires GPU_SAMPLER handle");
return false;
}
sampler_.handle = handle;
sampler_.descriptor = context.GetDevice().GetGpuResourceManager().GetSamplerDescriptor(handle);
sampler_.isDefault = false;
} else {
sampler_ = GetDefaultSampler();
}
auto& desc = sampler_.descriptor;
StartTransaction();
bool changed = SetSamplerValue<SamplerFilter>(GetExistingProperty(MAG_FILTER_PROPERTY), desc.magFilter);
changed |= SetSamplerValue<SamplerFilter>(GetExistingProperty(MIN_FILTER_PROPERTY), desc.minFilter);
changed |= SetSamplerValue<SamplerFilter>(GetExistingProperty(MIP_MAP_MODE_PROPERTY), desc.mipMapMode);
changed |= SetSamplerValue<SamplerAddressMode>(GetExistingProperty(ADDRESS_MODE_U_PROPERTY), desc.addressModeU);
changed |= SetSamplerValue<SamplerAddressMode>(GetExistingProperty(ADDRESS_MODE_V_PROPERTY), desc.addressModeV);
changed |= SetSamplerValue<SamplerAddressMode>(GetExistingProperty(ADDRESS_MODE_W_PROPERTY), desc.addressModeW);
EndTransaction(changed);
return false;
}
RENDER_NS::RenderHandleReference Sampler::GetHandleFromSampler(RENDER_NS::IRenderContext& context) const
{
if (sampler_.handle) {
return GetSamplerHandle(sampler_);
}
auto info = GetDefaultSampler();
auto& desc = info.descriptor;
auto isset = GetSamplerValue<SamplerFilter>(GetExistingProperty(MAG_FILTER_PROPERTY), desc.magFilter);
isset |= GetSamplerValue<SamplerFilter>(GetExistingProperty(MIN_FILTER_PROPERTY), desc.minFilter);
isset |= GetSamplerValue<SamplerFilter>(GetExistingProperty(MIP_MAP_MODE_PROPERTY), desc.mipMapMode);
isset |= GetSamplerValue<SamplerAddressMode>(GetExistingProperty(ADDRESS_MODE_U_PROPERTY), desc.addressModeU);
isset |= GetSamplerValue<SamplerAddressMode>(GetExistingProperty(ADDRESS_MODE_V_PROPERTY), desc.addressModeV);
isset |= GetSamplerValue<SamplerAddressMode>(GetExistingProperty(ADDRESS_MODE_W_PROPERTY), desc.addressModeW);
sampler_.isDefault = !isset;
sampler_.handle = sampler_.isDefault ? info.handle : context.GetDevice().GetGpuResourceManager().Create(desc);
sampler_.descriptor = BASE_NS::move(desc);
return GetSamplerHandle(sampler_);
}
SCENE_END_NAMESPACE()