// Copyright 2017 The NXT Authors // // 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 "backend/Texture.h" #include "backend/Device.h" #include "common/Assert.h" namespace backend { uint32_t TextureFormatPixelSize(nxt::TextureFormat format) { switch (format) { case nxt::TextureFormat::R8G8B8A8Unorm: return 4; case nxt::TextureFormat::D32FloatS8Uint: return 8; default: UNREACHABLE(); } } bool TextureFormatHasDepth(nxt::TextureFormat format) { switch (format) { case nxt::TextureFormat::R8G8B8A8Unorm: return false; case nxt::TextureFormat::D32FloatS8Uint: return true; default: UNREACHABLE(); } } bool TextureFormatHasStencil(nxt::TextureFormat format) { switch (format) { case nxt::TextureFormat::R8G8B8A8Unorm: return false; case nxt::TextureFormat::D32FloatS8Uint: return true; default: UNREACHABLE(); } } bool TextureFormatHasDepthOrStencil(nxt::TextureFormat format) { switch (format) { case nxt::TextureFormat::R8G8B8A8Unorm: return false; case nxt::TextureFormat::D32FloatS8Uint: return true; default: UNREACHABLE(); } } // TextureBase TextureBase::TextureBase(TextureBuilder* builder) : device(builder->device), dimension(builder->dimension), format(builder->format), width(builder->width), height(builder->height), depth(builder->depth), numMipLevels(builder->numMipLevels), allowedUsage(builder->allowedUsage), currentUsage(builder->currentUsage) { } DeviceBase* TextureBase::GetDevice() { return device; } nxt::TextureDimension TextureBase::GetDimension() const { return dimension; } nxt::TextureFormat TextureBase::GetFormat() const { return format; } uint32_t TextureBase::GetWidth() const { return width; } uint32_t TextureBase::GetHeight() const { return height; } uint32_t TextureBase::GetDepth() const { return depth; } uint32_t TextureBase::GetNumMipLevels() const { return numMipLevels; } nxt::TextureUsageBit TextureBase::GetAllowedUsage() const { return allowedUsage; } nxt::TextureUsageBit TextureBase::GetUsage() const { return currentUsage; } TextureViewBuilder* TextureBase::CreateTextureViewBuilder() { return new TextureViewBuilder(device, this); } bool TextureBase::IsFrozen() const { return frozen; } bool TextureBase::HasFrozenUsage(nxt::TextureUsageBit usage) const { return frozen && (usage & allowedUsage); } bool TextureBase::IsUsagePossible(nxt::TextureUsageBit allowedUsage, nxt::TextureUsageBit usage) { bool allowed = (usage & allowedUsage) == usage; bool singleUse = nxt::HasZeroOrOneBits(usage); return allowed && singleUse; } bool TextureBase::IsTransitionPossible(nxt::TextureUsageBit usage) const { if (frozen) { return false; } if (currentUsage == nxt::TextureUsageBit::Present) { return false; } return IsUsagePossible(allowedUsage, usage); } void TextureBase::UpdateUsageInternal(nxt::TextureUsageBit usage) { ASSERT(IsTransitionPossible(usage)); currentUsage = usage; } void TextureBase::TransitionUsage(nxt::TextureUsageBit usage) { if (!IsTransitionPossible(usage)) { device->HandleError("Texture frozen or usage not allowed"); return; } TransitionUsageImpl(currentUsage, usage); currentUsage = usage; } void TextureBase::FreezeUsage(nxt::TextureUsageBit usage) { if (!IsTransitionPossible(usage)) { device->HandleError("Texture frozen or usage not allowed"); return; } allowedUsage = usage; TransitionUsageImpl(currentUsage, usage); currentUsage = usage; frozen = true; } // TextureBuilder enum TextureSetProperties { TEXTURE_PROPERTY_DIMENSION = 0x1, TEXTURE_PROPERTY_EXTENT = 0x2, TEXTURE_PROPERTY_FORMAT = 0x4, TEXTURE_PROPERTY_MIP_LEVELS = 0x8, TEXTURE_PROPERTY_ALLOWED_USAGE = 0x10, TEXTURE_PROPERTY_INITIAL_USAGE = 0x20, }; TextureBuilder::TextureBuilder(DeviceBase* device) : Builder(device) { } TextureBase* TextureBuilder::GetResultImpl() { constexpr int allProperties = TEXTURE_PROPERTY_DIMENSION | TEXTURE_PROPERTY_EXTENT | TEXTURE_PROPERTY_FORMAT | TEXTURE_PROPERTY_MIP_LEVELS | TEXTURE_PROPERTY_ALLOWED_USAGE; if ((propertiesSet & allProperties) != allProperties) { HandleError("Texture missing properties"); return nullptr; } if (!TextureBase::IsUsagePossible(allowedUsage, currentUsage)) { HandleError("Initial texture usage is not allowed"); return nullptr; } // TODO(cwallez@chromium.org): check stuff based on the dimension return device->CreateTexture(this); } void TextureBuilder::SetDimension(nxt::TextureDimension dimension) { if ((propertiesSet & TEXTURE_PROPERTY_DIMENSION) != 0) { HandleError("Texture dimension property set multiple times"); return; } propertiesSet |= TEXTURE_PROPERTY_DIMENSION; this->dimension = dimension; } void TextureBuilder::SetExtent(uint32_t width, uint32_t height, uint32_t depth) { if ((propertiesSet & TEXTURE_PROPERTY_EXTENT) != 0) { HandleError("Texture extent property set multiple times"); return; } if (width == 0 || height == 0 || depth == 0) { HandleError("Cannot create an empty texture"); return; } propertiesSet |= TEXTURE_PROPERTY_EXTENT; this->width = width; this->height = height; this->depth = depth; } void TextureBuilder::SetFormat(nxt::TextureFormat format) { if ((propertiesSet & TEXTURE_PROPERTY_FORMAT) != 0) { HandleError("Texture format property set multiple times"); return; } propertiesSet |= TEXTURE_PROPERTY_FORMAT; this->format = format; } void TextureBuilder::SetMipLevels(uint32_t numMipLevels) { if ((propertiesSet & TEXTURE_PROPERTY_MIP_LEVELS) != 0) { HandleError("Texture mip levels property set multiple times"); return; } propertiesSet |= TEXTURE_PROPERTY_MIP_LEVELS; this->numMipLevels = numMipLevels; } void TextureBuilder::SetAllowedUsage(nxt::TextureUsageBit usage) { if ((propertiesSet & TEXTURE_PROPERTY_ALLOWED_USAGE) != 0) { HandleError("Texture allowed usage property set multiple times"); return; } propertiesSet |= TEXTURE_PROPERTY_ALLOWED_USAGE; this->allowedUsage = usage; } void TextureBuilder::SetInitialUsage(nxt::TextureUsageBit usage) { if ((propertiesSet & TEXTURE_PROPERTY_INITIAL_USAGE) != 0) { HandleError("Texture initial usage property set multiple times"); return; } propertiesSet |= TEXTURE_PROPERTY_INITIAL_USAGE; this->currentUsage = usage; } // TextureViewBase TextureViewBase::TextureViewBase(TextureViewBuilder* builder) : texture(builder->texture) { } TextureBase* TextureViewBase::GetTexture() { return texture.Get(); } // TextureViewBuilder TextureViewBuilder::TextureViewBuilder(DeviceBase* device, TextureBase* texture) : Builder(device), texture(texture) { } TextureViewBase* TextureViewBuilder::GetResultImpl() { return device->CreateTextureView(this); } }