blob: b6f66f5ef0d108255d168eb0458f34cecebfca43 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_GRAPHICS_LIB_MAGMA_SRC_MAGMA_UTIL_GPU_MAPPING_H
#define SRC_GRAPHICS_LIB_MAGMA_SRC_MAGMA_UTIL_GPU_MAPPING_H
#include <lib/magma/platform/platform_bus_mapper.h>
#include <lib/magma/util/macros.h>
#include <lib/magma/util/utils.h>
#include <memory>
#include <vector>
#include "accessor.h"
namespace magma {
// GpuMappingView exposes a non-mutable interface to a GpuMapping.
template <typename Buffer>
class GpuMappingView {
public:
using BufferType = Buffer;
GpuMappingView(std::shared_ptr<Buffer> buffer, uint64_t gpu_addr, uint64_t offset,
uint64_t length)
: buffer_(std::move(buffer)), gpu_addr_(gpu_addr), offset_(offset), length_(length) {}
uint64_t gpu_addr() const { return gpu_addr_; }
uint64_t offset() const { return offset_; }
// Length of a GpuMapping is mutable; this method is racy if called from a thread other
// than the connection thread.
uint64_t length() const { return length_; }
uint64_t BufferId() const { return BufferAccessor<Buffer>::platform_buffer(buffer_.get())->id(); }
uint64_t BufferSize() const {
return BufferAccessor<Buffer>::platform_buffer(buffer_.get())->size();
}
bool Copy(std::vector<uint32_t>* buffer_out) const;
protected:
~GpuMappingView() = default;
std::shared_ptr<Buffer> buffer_;
const uint64_t gpu_addr_;
const uint64_t offset_;
uint64_t length_;
};
template <typename GpuMapping>
class AddressSpace;
// GpuMapping is created by a connection thread, and mutated only by that connection thread.
// However, shared references to GpuMapping may be taken by command buffers, keeping them alive
// while the mappings are in flight.
// Therefore, GpuMappings can be destroyed from the device thread, if the connection has removed
// all its references.
// Mutation of the page tables in an AddressSpace is therefore thread locked.
template <typename Buffer>
class GpuMapping : public GpuMappingView<Buffer> {
public:
GpuMapping(std::shared_ptr<AddressSpace<GpuMapping>> address_space,
std::shared_ptr<Buffer> buffer, uint64_t offset, uint64_t length, uint64_t gpu_addr,
std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping)
: GpuMappingView<Buffer>(std::move(buffer), gpu_addr, offset, length),
address_space_(address_space) {
bus_mappings_.emplace_back(std::move(bus_mapping));
}
~GpuMapping() { Release(nullptr); }
Buffer* buffer() { return GpuMappingView<Buffer>::buffer_.get(); }
std::weak_ptr<AddressSpace<GpuMapping>> address_space() const { return address_space_; }
// Add the given |bus_mapping|.
// Note that length() changes as a result.
void Grow(std::unique_ptr<magma::PlatformBusMapper::BusMapping> bus_mapping) {
GpuMappingView<Buffer>::length_ += bus_mapping->page_count() * magma::page_size();
bus_mappings_.emplace_back(std::move(bus_mapping));
}
// Releases the gpu mapping, returns all bus mappings in |bus_mappings_out|.
// Called by the device thread (via destructor), or connection thread.
bool Release(
std::vector<std::unique_ptr<magma::PlatformBusMapper::BusMapping>>* bus_mappings_out);
private:
std::weak_ptr<AddressSpace<GpuMapping>> address_space_;
std::vector<std::unique_ptr<magma::PlatformBusMapper::BusMapping>> bus_mappings_;
};
template <typename Buffer>
bool GpuMappingView<Buffer>::Copy(std::vector<uint32_t>* buffer_out) const {
auto platform_buffer = BufferAccessor<Buffer>::platform_buffer(buffer_.get());
void* data;
if (!platform_buffer->MapCpu(&data))
return MAGMA_DRETF(false, "couldn't map buffer");
buffer_out->resize(platform_buffer->size());
std::memcpy(buffer_out->data(), data, buffer_out->size());
platform_buffer->UnmapCpu();
return true;
}
template <typename Buffer>
bool GpuMapping<Buffer>::Release(
std::vector<std::unique_ptr<magma::PlatformBusMapper::BusMapping>>* bus_mappings_out) {
auto address_space = address_space_.lock();
bool success = true;
if (address_space) {
uint64_t addr = GpuMappingView<Buffer>::gpu_addr();
MAGMA_DASSERT(bus_mappings_.size());
if (!bus_mappings_[0]) {
if (!address_space->Clear(addr, nullptr))
success = false;
} else {
for (auto& bus_mapping : bus_mappings_) {
MAGMA_DASSERT(bus_mapping);
if (!address_space->Clear(addr, bus_mapping.get()))
success = false;
addr += bus_mapping->page_count() * magma::page_size();
}
}
if (!address_space->Free(GpuMappingView<Buffer>::gpu_addr()))
success = false;
}
GpuMappingView<Buffer>::buffer_.reset();
address_space_.reset();
GpuMappingView<Buffer>::length_ = 0;
if (bus_mappings_out) {
*bus_mappings_out = std::move(bus_mappings_);
}
bus_mappings_.clear();
return success;
}
} // namespace magma
#endif // SRC_GRAPHICS_LIB_MAGMA_SRC_MAGMA_UTIL_GPU_MAPPING_H